diff --git a/scripts/modules/bpy_extras/object_utils.py b/scripts/modules/bpy_extras/object_utils.py index b78d256f99d..de5a93784fc 100644 --- a/scripts/modules/bpy_extras/object_utils.py +++ b/scripts/modules/bpy_extras/object_utils.py @@ -11,6 +11,7 @@ __all__ = ( "object_add_grid_scale", "object_add_grid_scale_apply_operator", "world_to_camera_view", + "object_verify_active_shape_key_is_editable", ) @@ -263,3 +264,26 @@ def world_to_camera_view(scene, obj, coord): y = (co_local.y - min_y) / (max_y - min_y) return Vector((x, y, z)) + + +def object_report_if_active_shape_key_is_locked(obj, operator): + """ + Checks if the active shape key of the specified object is locked, and reports an error if so. + + If the object has no shape keys, there is nothing to lock, and the function returns False. + + :arg obj: Object to check. + :type obj: :class:`bpy.types.Object` + :arg operator: Currently running operator to report the error through. Use None to suppress emitting the message. + :type operator: :class:`bpy.types.Operator` + :return: True if the shape key was locked. + """ + key = obj.active_shape_key + + if key and key.lock_shape: + if operator: + operator.report({'ERROR'}, "The active shape key of %s is locked" % obj.name) + + return True + + return False diff --git a/scripts/startup/bl_operators/view3d.py b/scripts/startup/bl_operators/view3d.py index aa71b1f710b..0042617f367 100644 --- a/scripts/startup/bl_operators/view3d.py +++ b/scripts/startup/bl_operators/view3d.py @@ -8,6 +8,7 @@ from bpy.props import ( BoolProperty, EnumProperty, ) +from bpy_extras.object_utils import object_report_if_active_shape_key_is_locked class VIEW3D_OT_edit_mesh_extrude_individual_move(Operator): @@ -21,6 +22,9 @@ class VIEW3D_OT_edit_mesh_extrude_individual_move(Operator): return (obj is not None and obj.mode == 'EDIT') def execute(self, context): + if object_report_if_active_shape_key_is_locked(context.object, self): + return {'CANCELLED'} + mesh = context.object.data select_mode = context.tool_settings.mesh_select_mode @@ -83,7 +87,10 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator): return (obj is not None and obj.mode == 'EDIT') @staticmethod - def extrude_region(context, use_vert_normals, dissolve_and_intersect): + def extrude_region(operator, context, use_vert_normals, dissolve_and_intersect): + if object_report_if_active_shape_key_is_locked(context.object, operator): + return {'CANCELLED'} + mesh = context.object.data totface = mesh.total_face_sel @@ -146,7 +153,7 @@ class VIEW3D_OT_edit_mesh_extrude_move(Operator): def execute(self, context): return VIEW3D_OT_edit_mesh_extrude_move.extrude_region( - context, False, self.dissolve_and_intersect) + self, context, False, self.dissolve_and_intersect) def invoke(self, context, _event): return self.execute(context) @@ -163,7 +170,7 @@ class VIEW3D_OT_edit_mesh_extrude_shrink_fatten(Operator): return (obj is not None and obj.mode == 'EDIT') def execute(self, context): - return VIEW3D_OT_edit_mesh_extrude_move.extrude_region(context, True, False) + return VIEW3D_OT_edit_mesh_extrude_move.extrude_region(self, context, True, False) def invoke(self, context, _event): return self.execute(context) @@ -179,7 +186,9 @@ class VIEW3D_OT_edit_mesh_extrude_manifold_normal(Operator): obj = context.active_object return (obj is not None and obj.mode == 'EDIT') - def execute(self, _context): + def execute(self, context): + if object_report_if_active_shape_key_is_locked(context.object, self): + return {'CANCELLED'} bpy.ops.mesh.extrude_manifold( 'INVOKE_REGION_WIN', MESH_OT_extrude_region={ diff --git a/scripts/startup/bl_ui/properties_data_mesh.py b/scripts/startup/bl_ui/properties_data_mesh.py index d8a9488bc4e..a1b1cfd5cb4 100644 --- a/scripts/startup/bl_ui/properties_data_mesh.py +++ b/scripts/startup/bl_ui/properties_data_mesh.py @@ -73,6 +73,9 @@ class MESH_MT_shape_key_context_menu(Menu): props.all = True props.apply_mix = True layout.separator() + layout.operator("object.shape_key_lock", icon='LOCKED', text="Lock All").action = 'LOCK' + layout.operator("object.shape_key_lock", icon='UNLOCKED', text="Unlock All").action = 'UNLOCK' + layout.separator() layout.operator("object.shape_key_move", icon='TRIA_UP_BAR', text="Move to Top").type = 'TOP' layout.operator("object.shape_key_move", icon='TRIA_DOWN_BAR', text="Move to Bottom").type = 'BOTTOM' @@ -132,6 +135,7 @@ class MESH_UL_shape_keys(UIList): else: row.label(text="") row.prop(key_block, "mute", text="", emboss=False) + row.prop(key_block, "lock_shape", text="", emboss=False) elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index fbc3a1a8b3e..81bbfd40466 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -88,9 +88,9 @@ struct KeyBlock *BKE_keyblock_add(struct Key *key, const char *name); */ struct KeyBlock *BKE_keyblock_add_ctime(struct Key *key, const char *name, bool do_force); /** - * Get the appropriate #KeyBlock given an index. + * Get the appropriate #KeyBlock given an index (0 refers to the basis key). Key may be null. */ -struct KeyBlock *BKE_keyblock_from_key(struct Key *key, int index); +struct KeyBlock *BKE_keyblock_find_by_index(struct Key *key, int index); /** * Get the appropriate #KeyBlock given a name to search for. */ diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 6f3994d0954..7b3e15002aa 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -357,9 +357,9 @@ static float (*get_orco_coords(Object *ob, BMEditMesh *em, int layer, int *free) if (!em) { ClothModifierData *clmd = (ClothModifierData *)BKE_modifiers_findby_type( ob, eModifierType_Cloth); - if (clmd) { - KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ob), - clmd->sim_parms->shapekey_rest); + if (clmd && clmd->sim_parms->shapekey_rest) { + KeyBlock *kb = BKE_keyblock_find_by_index(BKE_key_from_object(ob), + clmd->sim_parms->shapekey_rest); if (kb && kb->data) { return (float(*)[3])kb->data; diff --git a/source/blender/blenkernel/intern/ipo.cc b/source/blender/blenkernel/intern/ipo.cc index 0fd62b05ade..7c71206a88e 100644 --- a/source/blender/blenkernel/intern/ipo.cc +++ b/source/blender/blenkernel/intern/ipo.cc @@ -443,7 +443,7 @@ static char *shapekey_adrcodes_to_paths(ID *id, int adrcode, int * /*r_array_ind else { /* Find the name of the ShapeKey (i.e. KeyBlock) to look for */ Key *key = (Key *)id; - KeyBlock *kb = BKE_keyblock_from_key(key, adrcode); + KeyBlock *kb = BKE_keyblock_find_by_index(key, adrcode); /* setting that we alter is the "value" (i.e. keyblock.curval) */ if (kb) { diff --git a/source/blender/blenkernel/intern/key.cc b/source/blender/blenkernel/intern/key.cc index ad469116792..c5656880eac 100644 --- a/source/blender/blenkernel/intern/key.cc +++ b/source/blender/blenkernel/intern/key.cc @@ -1897,12 +1897,7 @@ KeyBlock *BKE_keyblock_from_object(Object *ob) { Key *key = BKE_key_from_object(ob); - if (key) { - KeyBlock *kb = static_cast(BLI_findlink(&key->block, ob->shapenr - 1)); - return kb; - } - - return nullptr; + return BKE_keyblock_find_by_index(key, ob->shapenr - 1); } KeyBlock *BKE_keyblock_from_object_reference(Object *ob) @@ -1916,21 +1911,13 @@ KeyBlock *BKE_keyblock_from_object_reference(Object *ob) return nullptr; } -KeyBlock *BKE_keyblock_from_key(Key *key, int index) +KeyBlock *BKE_keyblock_find_by_index(Key *key, int index) { - if (key) { - KeyBlock *kb = static_cast(key->block.first); - - for (int i = 1; i < key->totkey; i++) { - kb = kb->next; - - if (index == i) { - return kb; - } - } + if (!key) { + return nullptr; } - return nullptr; + return static_cast(BLI_findlink(&key->block, index)); } KeyBlock *BKE_keyblock_find_name(Key *key, const char name[]) diff --git a/source/blender/editors/curve/editcurve.cc b/source/blender/editors/curve/editcurve.cc index ad64c671772..c917d2be897 100644 --- a/source/blender/editors/curve/editcurve.cc +++ b/source/blender/editors/curve/editcurve.cc @@ -86,6 +86,13 @@ ListBase *object_editcurve_get(Object *ob) return nullptr; } +KeyBlock *ED_curve_get_edit_shape_key(const Curve *cu) +{ + BLI_assert(cu->editnurb); + + return BKE_keyblock_find_by_index(cu->key, cu->editnurb->shapenr - 1); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -2701,8 +2708,17 @@ static int set_radius_exec(bContext *C, wmOperator *op) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); + int totobjects = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; + + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + ListBase *editnurb = object_editcurve_get(obedit); BezTriple *bezt; BPoint *bp; @@ -2732,7 +2748,7 @@ static int set_radius_exec(bContext *C, wmOperator *op) MEM_freeN(objects); - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void CURVE_OT_radius_set(wmOperatorType *ot) @@ -2804,7 +2820,7 @@ static void smooth_single_bp(BPoint *bp, } } -static int smooth_exec(bContext *C, wmOperator * /*op*/) +static int smooth_exec(bContext *C, wmOperator *op) { const float factor = 1.0f / 6.0f; const Scene *scene = CTX_data_scene(C); @@ -2813,8 +2829,17 @@ static int smooth_exec(bContext *C, wmOperator * /*op*/) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); + int totobjects = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; + + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + ListBase *editnurb = object_editcurve_get(obedit); int a, a_end; @@ -2892,7 +2917,7 @@ static int smooth_exec(bContext *C, wmOperator * /*op*/) MEM_freeN(objects); - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void CURVE_OT_smooth(wmOperatorType *ot) @@ -3146,7 +3171,7 @@ void CURVE_OT_smooth_weight(wmOperatorType *ot) /** \name Smooth Radius Operator * \{ */ -static int curve_smooth_radius_exec(bContext *C, wmOperator * /*op*/) +static int curve_smooth_radius_exec(bContext *C, wmOperator *op) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -3154,8 +3179,17 @@ static int curve_smooth_radius_exec(bContext *C, wmOperator * /*op*/) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); + int totobjects = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; + + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + ListBase *editnurb = object_editcurve_get(obedit); curve_smooth_value(editnurb, offsetof(BezTriple, radius), offsetof(BPoint, radius)); @@ -3166,7 +3200,7 @@ static int curve_smooth_radius_exec(bContext *C, wmOperator * /*op*/) MEM_freeN(objects); - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void CURVE_OT_smooth_radius(wmOperatorType *ot) @@ -3190,7 +3224,7 @@ void CURVE_OT_smooth_radius(wmOperatorType *ot) /** \name Smooth Tilt Operator * \{ */ -static int curve_smooth_tilt_exec(bContext *C, wmOperator * /*op*/) +static int curve_smooth_tilt_exec(bContext *C, wmOperator *op) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -3198,8 +3232,17 @@ static int curve_smooth_tilt_exec(bContext *C, wmOperator * /*op*/) Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); + int totobjects = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; + + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + ListBase *editnurb = object_editcurve_get(obedit); curve_smooth_value(editnurb, offsetof(BezTriple, tilt), offsetof(BPoint, tilt)); @@ -3210,7 +3253,7 @@ static int curve_smooth_tilt_exec(bContext *C, wmOperator * /*op*/) MEM_freeN(objects); - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void CURVE_OT_smooth_tilt(wmOperatorType *ot) @@ -4047,6 +4090,9 @@ static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op) uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); + + int totobjects = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; Curve *cu = static_cast(obedit->data); @@ -4055,6 +4101,12 @@ static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op) continue; } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + ListBase *editnurb = object_editcurve_get(obedit); BKE_nurbList_handles_recalculate(editnurb, calc_length, SELECT); @@ -4062,7 +4114,7 @@ static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op) DEG_id_tag_update(static_cast(obedit->data), 0); } MEM_freeN(objects); - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void CURVE_OT_normals_make_consistent(wmOperatorType *ot) @@ -7042,7 +7094,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) /** \name Clear Tilt Operator * \{ */ -static int clear_tilt_exec(bContext *C, wmOperator * /*op*/) +static int clear_tilt_exec(bContext *C, wmOperator *op) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -7051,6 +7103,9 @@ static int clear_tilt_exec(bContext *C, wmOperator * /*op*/) uint objects_len; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); + + int totobjects = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; Curve *cu = static_cast(obedit->data); @@ -7059,6 +7114,12 @@ static int clear_tilt_exec(bContext *C, wmOperator * /*op*/) continue; } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + ListBase *editnurb = object_editcurve_get(obedit); BezTriple *bezt; BPoint *bp; @@ -7091,7 +7152,7 @@ static int clear_tilt_exec(bContext *C, wmOperator * /*op*/) DEG_id_tag_update(static_cast(obedit->data), 0); } MEM_freeN(objects); - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void CURVE_OT_tilt_clear(wmOperatorType *ot) diff --git a/source/blender/editors/include/ED_curve.hh b/source/blender/editors/include/ED_curve.hh index 18db65b033f..b160f4e53ea 100644 --- a/source/blender/editors/include/ED_curve.hh +++ b/source/blender/editors/include/ED_curve.hh @@ -13,6 +13,7 @@ struct Base; struct BezTriple; struct Curve; struct EditNurb; +struct KeyBlock; struct ListBase; struct Main; struct Nurb; @@ -35,6 +36,8 @@ void ED_keymap_curve(wmKeyConfig *keyconf); ListBase *object_editcurve_get(Object *ob); +KeyBlock *ED_curve_get_edit_shape_key(const Curve *cu); + /** * Load editNurb in object. */ diff --git a/source/blender/editors/include/ED_lattice.hh b/source/blender/editors/include/ED_lattice.hh index 0ec7aca044f..834688b8543 100644 --- a/source/blender/editors/include/ED_lattice.hh +++ b/source/blender/editors/include/ED_lattice.hh @@ -9,6 +9,8 @@ #pragma once struct Base; +struct KeyBlock; +struct Lattice; struct Object; struct SelectPick_Params; struct UndoType; @@ -19,6 +21,8 @@ struct wmKeyConfig; void ED_operatortypes_lattice(); void ED_keymap_lattice(wmKeyConfig *keyconf); +KeyBlock *ED_lattice_get_edit_shape_key(const Lattice *latt); + /* `editlattice_select.cc` */ bool ED_lattice_flags_set(Object *obedit, int flag); diff --git a/source/blender/editors/include/ED_mesh.hh b/source/blender/editors/include/ED_mesh.hh index 77ed9671c81..b85cbf233d2 100644 --- a/source/blender/editors/include/ED_mesh.hh +++ b/source/blender/editors/include/ED_mesh.hh @@ -23,6 +23,7 @@ struct BMeshNormalsUpdate_Params; struct Base; struct Depsgraph; struct ID; +struct KeyBlock; struct MDeformVert; struct Mesh; struct Object; @@ -554,6 +555,8 @@ int ED_mesh_color_add( void ED_mesh_report_mirror(wmOperator *op, int totmirr, int totfail); void ED_mesh_report_mirror_ex(wmOperator *op, int totmirr, int totfail, char selectmode); +KeyBlock *ED_mesh_get_edit_shape_key(const Mesh *me); + /** * Returns the pinned mesh, the mesh from the pinned object, or the mesh from the active object. */ diff --git a/source/blender/editors/include/ED_object.hh b/source/blender/editors/include/ED_object.hh index 3b0820df312..3ef569d8899 100644 --- a/source/blender/editors/include/ED_object.hh +++ b/source/blender/editors/include/ED_object.hh @@ -63,6 +63,29 @@ Object **ED_object_array_in_mode_or_selected(bContext *C, void *filter_user_data, uint *r_objects_len); +/* `object_shapekey.cc` */ + +/** + * Checks if the currently active Edit Mode on the object is targeting a locked shape key, + * and produces an error message if so (unless \a reports is null). + * \return true if the shape key was locked. + */ +bool ED_object_edit_report_if_shape_key_is_locked(const Object *obedit, ReportList *reports); + +/** + * Checks if the active shape key of the object is locked, and produces an error message + * if so (unless \a reports is null). + * \return true if the shape key was locked. + */ +bool ED_object_report_if_active_shape_key_is_locked(Object *ob, ReportList *reports); + +/** + * Checks if any of the shape keys of the object are locked, and produces an error message if so + * (unless \a reports is null). + * \return true if a shape key was locked. + */ +bool ED_object_report_if_any_shape_key_is_locked(Object *ob, ReportList *reports); + /* `object_utils.cc` */ bool ED_object_calc_active_center_for_editmode(Object *obedit, diff --git a/source/blender/editors/include/ED_sculpt.hh b/source/blender/editors/include/ED_sculpt.hh index 2d2ee973b95..abae81d6c94 100644 --- a/source/blender/editors/include/ED_sculpt.hh +++ b/source/blender/editors/include/ED_sculpt.hh @@ -10,6 +10,7 @@ struct ARegion; struct Object; +struct ReportList; struct UndoType; struct ViewContext; struct bContext; @@ -19,6 +20,13 @@ struct wmKeyConfig; /* sculpt.cc */ +/** + * Checks if the currently active Sculpt Mode on the object is targeting a locked shape key, + * and produces an error message if so (unless \a reports is null). + * \return true if the shape key was locked. + */ +bool ED_sculpt_report_if_shape_key_is_locked(const Object *ob, ReportList *reports); + void ED_operatortypes_sculpt(); void ED_keymap_sculpt(wmKeyConfig *keyconf); diff --git a/source/blender/editors/include/ED_view3d.hh b/source/blender/editors/include/ED_view3d.hh index 449262d8ed9..d3cd26de431 100644 --- a/source/blender/editors/include/ED_view3d.hh +++ b/source/blender/editors/include/ED_view3d.hh @@ -53,6 +53,7 @@ struct rcti; struct wmEvent; struct wmGizmo; struct wmKeyMapItem; +struct wmOperator; struct wmWindow; struct wmWindowManager; @@ -288,6 +289,7 @@ ENUM_OPERATORS(eV3DProjTest, V3D_PROJ_TEST_CLIP_CONTENT); /* `view3d_snap.cc` */ bool ED_view3d_snap_selected_to_location(bContext *C, + wmOperator *op, const float snap_target_global[3], int pivot_point); diff --git a/source/blender/editors/lattice/editlattice_tools.cc b/source/blender/editors/lattice/editlattice_tools.cc index 035e11f5cdb..12ce3c1c9b9 100644 --- a/source/blender/editors/lattice/editlattice_tools.cc +++ b/source/blender/editors/lattice/editlattice_tools.cc @@ -25,6 +25,7 @@ #include "DEG_depsgraph.hh" +#include "ED_object.hh" #include "ED_screen.hh" #include "WM_api.hh" @@ -48,7 +49,7 @@ static bool make_regular_poll(bContext *C) return (ob && ob->type == OB_LATTICE); } -static int make_regular_exec(bContext *C, wmOperator * /*op*/) +static int make_regular_exec(bContext *C, wmOperator *op) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -67,6 +68,10 @@ static int make_regular_exec(bContext *C, wmOperator * /*op*/) continue; } + if (ED_object_edit_report_if_shape_key_is_locked(ob, op->reports)) { + continue; + } + BKE_lattice_resize(lt->editlatt->latt, lt->pntsu, lt->pntsv, lt->pntsw, nullptr); DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); @@ -219,6 +224,10 @@ static int lattice_flip_exec(bContext *C, wmOperator *op) lt = (Lattice *)obedit->data; lt = lt->editlatt->latt; + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + numU = lt->pntsu; numV = lt->pntsv; numW = lt->pntsw; diff --git a/source/blender/editors/lattice/lattice_ops.cc b/source/blender/editors/lattice/lattice_ops.cc index cc1cfe36029..695be650210 100644 --- a/source/blender/editors/lattice/lattice_ops.cc +++ b/source/blender/editors/lattice/lattice_ops.cc @@ -6,8 +6,11 @@ * \ingroup edlattice */ +#include "DNA_lattice_types.h" #include "DNA_scene_types.h" +#include "BKE_key.h" + #include "WM_api.hh" #include "ED_lattice.hh" @@ -32,3 +35,10 @@ void ED_keymap_lattice(wmKeyConfig *keyconf) wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Lattice", SPACE_EMPTY, RGN_TYPE_WINDOW); keymap->poll = ED_operator_editlattice; } + +KeyBlock *ED_lattice_get_edit_shape_key(const Lattice *latt) +{ + BLI_assert(latt->editlatt); + + return BKE_keyblock_find_by_index(latt->key, latt->editlatt->shapenr - 1); +} diff --git a/source/blender/editors/mesh/editmesh_tools.cc b/source/blender/editors/mesh/editmesh_tools.cc index 7ca62eaa271..15bec5861ab 100644 --- a/source/blender/editors/mesh/editmesh_tools.cc +++ b/source/blender/editors/mesh/editmesh_tools.cc @@ -1804,13 +1804,22 @@ static int edbm_face_make_planar_exec(bContext *C, wmOperator *op) const int repeat = RNA_int_get(op->ptr, "repeat"); const float fac = RNA_float_get(op->ptr, "factor"); + int totobjects = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totfacesel == 0) { continue; } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + if (!EDBM_op_callf( em, op, "planar_faces faces=%hf iterations=%i factor=%f", BM_ELEM_SELECT, repeat, fac)) { @@ -1825,7 +1834,7 @@ static int edbm_face_make_planar_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void MESH_OT_face_make_planar(wmOperatorType *ot) @@ -2691,6 +2700,7 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + int tot_selected = 0, tot_locked = 0; uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); @@ -2706,6 +2716,13 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) continue; } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + tot_locked++; + continue; + } + + tot_selected++; + /* mirror before smooth */ if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) { EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology); @@ -2768,7 +2785,11 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - return OPERATOR_FINISHED; + if (tot_selected == 0 && !tot_locked) { + BKE_report(op->reports, RPT_WARNING, "No selected vertex"); + } + + return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void MESH_OT_vertices_smooth(wmOperatorType *ot) @@ -2808,7 +2829,7 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) { - int tot_unselected = 0; + int tot_selected = 0, tot_locked = 0; const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -2834,10 +2855,16 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0; if (em->bm->totvertsel == 0) { - tot_unselected++; continue; } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + tot_locked++; + continue; + } + + tot_selected++; + /* Mirror before smooth. */ if (((Mesh *)obedit->data)->symmetry & ME_SYMMETRY_X) { EDBM_verts_mirror_cache_begin(em, 0, false, true, false, use_topology); @@ -2879,12 +2906,11 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - if (tot_unselected == objects_len) { + if (tot_selected == 0 && !tot_locked) { BKE_report(op->reports, RPT_WARNING, "No selected vertex"); - return OPERATOR_CANCELLED; } - return OPERATOR_FINISHED; + return tot_selected ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void MESH_OT_vertices_smooth_laplacian(wmOperatorType *ot) @@ -3746,6 +3772,7 @@ static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); int tot_shapekeys = 0; int tot_selected_verts_objects = 0; + int tot_locked = 0; uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( @@ -3758,6 +3785,13 @@ static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op) if (em->bm->totvertsel == 0) { continue; } + + /* Check for locked shape keys. */ + if (ED_object_report_if_any_shape_key_is_locked(obedit, op->reports)) { + tot_locked++; + continue; + } + tot_selected_verts_objects++; const bool use_symmetry = (mesh->symmetry & ME_SYMMETRY_X) != 0; @@ -3785,7 +3819,9 @@ static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op) MEM_freeN(objects); if (tot_selected_verts_objects == 0) { - BKE_report(op->reports, RPT_ERROR, "No selected vertex"); + if (!tot_locked) { + BKE_report(op->reports, RPT_ERROR, "No selected vertex"); + } return OPERATOR_CANCELLED; } if (tot_shapekeys == 0) { @@ -3853,7 +3889,7 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op) kb_ref = static_cast(BLI_findlink(&key_ref->block, shape_ref)); } - int tot_selected_verts_objects = 0; + int tot_selected_verts_objects = 0, tot_locked = 0; uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( scene, view_layer, CTX_wm_view3d(C), &objects_len); @@ -3868,6 +3904,12 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op) if (em->bm->totvertsel == 0) { continue; } + + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + tot_locked++; + continue; + } + tot_selected_verts_objects++; if (!key) { @@ -3924,12 +3966,11 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op) } MEM_freeN(objects); - if (tot_selected_verts_objects == 0) { + if (tot_selected_verts_objects == 0 && !tot_locked) { BKE_report(op->reports, RPT_ERROR, "No selected vertex"); - return OPERATOR_CANCELLED; } - return OPERATOR_FINISHED; + return tot_selected_verts_objects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } static const EnumPropertyItem *shape_itemf(bContext *C, @@ -8010,7 +8051,7 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) const int axis_dir = RNA_enum_get(op->ptr, "direction"); /* Vertices stats (total over all selected objects). */ - int totvertfound = 0, totvertmirr = 0, totvertfail = 0; + int totvertfound = 0, totvertmirr = 0, totvertfail = 0, totobjects = 0; /* Axis. */ int axis = axis_dir % 3; @@ -8031,6 +8072,12 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) continue; } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + + totobjects++; + /* Only allocate memory after checking whether to skip object. */ int *index = static_cast(MEM_mallocN(bm->totvert * sizeof(*index), __func__)); @@ -8116,7 +8163,7 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) totvertmirr, totvertfail); } - else { + else if (totobjects) { BKE_reportf(op->reports, RPT_INFO, "%d already symmetrical, %d pairs mirrored", @@ -8124,7 +8171,7 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) totvertmirr); } - return OPERATOR_FINISHED; + return totobjects ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void MESH_OT_symmetry_snap(wmOperatorType *ot) diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index e17c310ee06..4dda9f78fed 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -19,6 +19,7 @@ #include "BKE_context.hh" #include "BKE_customdata.hh" #include "BKE_editmesh.hh" +#include "BKE_key.h" #include "BKE_mesh.hh" #include "BKE_mesh_runtime.hh" #include "BKE_report.h" @@ -1111,6 +1112,13 @@ void ED_mesh_report_mirror(wmOperator *op, int totmirr, int totfail) ED_mesh_report_mirror_ex(op, totmirr, totfail, SCE_SELECT_VERTEX); } +KeyBlock *ED_mesh_get_edit_shape_key(const Mesh *me) +{ + BLI_assert(me->edit_mesh && me->edit_mesh->bm); + + return BKE_keyblock_find_by_index(me->key, me->edit_mesh->bm->shapenr - 1); +} + Mesh *ED_mesh_context(bContext *C) { Mesh *mesh = static_cast(CTX_data_pointer_get_type(C, "mesh", &RNA_Mesh).data); diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index ebfc42e2fb8..87b2a83de40 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -4228,7 +4228,7 @@ static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) * * The caller is responsible for ensuring the selection state gives useful results. * Link/append does this using #FILE_AUTOSELECT. */ - ED_view3d_snap_selected_to_location(C, cursor, V3D_AROUND_ACTIVE); + ED_view3d_snap_selected_to_location(C, op, cursor, V3D_AROUND_ACTIVE); } } diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 618ccea9707..1d0c62c5300 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -329,6 +329,7 @@ void OBJECT_OT_shape_key_clear(struct wmOperatorType *ot); void OBJECT_OT_shape_key_retime(struct wmOperatorType *ot); void OBJECT_OT_shape_key_mirror(struct wmOperatorType *ot); void OBJECT_OT_shape_key_move(struct wmOperatorType *ot); +void OBJECT_OT_shape_key_lock(struct wmOperatorType *ot); /* `object_collection.cc` */ diff --git a/source/blender/editors/object/object_ops.cc b/source/blender/editors/object/object_ops.cc index 92e60622018..df916ed2f34 100644 --- a/source/blender/editors/object/object_ops.cc +++ b/source/blender/editors/object/object_ops.cc @@ -240,6 +240,7 @@ void ED_operatortypes_object() WM_operatortype_append(OBJECT_OT_shape_key_retime); WM_operatortype_append(OBJECT_OT_shape_key_mirror); WM_operatortype_append(OBJECT_OT_shape_key_move); + WM_operatortype_append(OBJECT_OT_shape_key_lock); WM_operatortype_append(OBJECT_OT_collection_add); WM_operatortype_append(OBJECT_OT_collection_link); diff --git a/source/blender/editors/object/object_random.cc b/source/blender/editors/object/object_random.cc index 007f3fc29c4..589c0fef0bf 100644 --- a/source/blender/editors/object/object_random.cc +++ b/source/blender/editors/object/object_random.cc @@ -24,6 +24,7 @@ #include "WM_api.hh" #include "WM_types.hh" +#include "ED_object.hh" #include "ED_transverts.hh" #include "object_intern.h" @@ -105,6 +106,10 @@ static int object_rand_verts_exec(bContext *C, wmOperator *op) mode |= TX_VERT_USE_NORMAL; } + if (ED_object_edit_report_if_shape_key_is_locked(ob_iter, op->reports)) { + continue; + } + ED_transverts_create_from_obedit(&tvs, ob_iter, mode); if (tvs.transverts_tot == 0) { continue; diff --git a/source/blender/editors/object/object_shapekey.cc b/source/blender/editors/object/object_shapekey.cc index 6e769c75be7..930149cb1c4 100644 --- a/source/blender/editors/object/object_shapekey.cc +++ b/source/blender/editors/object/object_shapekey.cc @@ -42,6 +42,8 @@ #include "BLI_sys_types.h" /* for intptr_t support */ +#include "ED_curve.hh" +#include "ED_lattice.hh" #include "ED_mesh.hh" #include "ED_object.hh" @@ -53,6 +55,82 @@ #include "object_intern.h" +/* -------------------------------------------------------------------- */ +/** \name Shape Key Lock Checks + * \{ */ + +bool ED_object_edit_report_if_shape_key_is_locked(const Object *obedit, ReportList *reports) +{ + KeyBlock *key_block; + + switch (obedit->type) { + case OB_MESH: + key_block = ED_mesh_get_edit_shape_key(static_cast(obedit->data)); + break; + case OB_SURF: + case OB_CURVES_LEGACY: + key_block = ED_curve_get_edit_shape_key(static_cast(obedit->data)); + break; + case OB_LATTICE: + key_block = ED_lattice_get_edit_shape_key(static_cast(obedit->data)); + break; + default: + return false; + } + + if (key_block && (key_block->flag & KEYBLOCK_LOCKED_SHAPE) != 0) { + if (reports) { + BKE_reportf(reports, RPT_ERROR, "The active shape key of %s is locked", obedit->id.name + 2); + } + return true; + } + + return false; +} + +bool ED_object_report_if_active_shape_key_is_locked(Object *ob, ReportList *reports) +{ + const KeyBlock *kb = BKE_keyblock_from_object(ob); + + if (kb && (kb->flag & KEYBLOCK_LOCKED_SHAPE) != 0) { + if (reports) { + BKE_reportf(reports, RPT_ERROR, "The active shape key of %s is locked", ob->id.name + 2); + } + return true; + } + + return false; +} + +static bool object_is_any_shape_key_locked(Object *ob) +{ + const Key *key = BKE_key_from_object(ob); + + if (key) { + LISTBASE_FOREACH (const KeyBlock *, kb, &key->block) { + if (kb->flag & KEYBLOCK_LOCKED_SHAPE) { + return true; + } + } + } + + return false; +} + +bool ED_object_report_if_any_shape_key_is_locked(Object *ob, ReportList *reports) +{ + if (object_is_any_shape_key_locked(ob)) { + if (reports) { + BKE_reportf(reports, RPT_ERROR, "The object %s has locked shape keys", ob->id.name + 2); + } + return true; + } + + return false; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Add Shape Key Function * \{ */ @@ -224,6 +302,15 @@ static bool shape_key_poll(bContext *C) !ID_IS_LINKED(data) && !ID_IS_OVERRIDE_LIBRARY(data)); } +static bool shape_key_exists_poll(bContext *C) +{ + Object *ob = ED_object_context(C); + + return (shape_key_poll(C) && + /* check a keyblock exists */ + (BKE_keyblock_from_object(ob) != nullptr)); +} + static bool shape_key_mode_poll(bContext *C) { Object *ob = ED_object_context(C); @@ -303,6 +390,10 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op) bool changed = false; if (RNA_boolean_get(op->ptr, "all")) { + if (ED_object_report_if_any_shape_key_is_locked(ob, op->reports)) { + return OPERATOR_CANCELLED; + } + if (RNA_boolean_get(op->ptr, "apply_mix")) { float *arr = BKE_key_evaluate_object_ex( ob, nullptr, nullptr, 0, static_cast(ob->data)); @@ -311,6 +402,10 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op) changed = BKE_object_shapekey_free(bmain, ob); } else { + if (ED_object_report_if_active_shape_key_is_locked(ob, op->reports)) { + return OPERATOR_CANCELLED; + } + changed = object_shapekey_remove(bmain, ob); } @@ -466,6 +561,10 @@ static int shape_key_mirror_exec(bContext *C, wmOperator *op) int totmirr = 0, totfail = 0; bool use_topology = RNA_boolean_get(op->ptr, "use_topology"); + if (ED_object_report_if_active_shape_key_is_locked(ob, op->reports)) { + return OPERATOR_CANCELLED; + } + if (!object_shape_key_mirror(C, ob, &totmirr, &totfail, use_topology)) { return OPERATOR_CANCELLED; } @@ -571,3 +670,89 @@ void OBJECT_OT_shape_key_move(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shape Key Lock (Unlock) Operator + * \{ */ + +enum { + SHAPE_KEY_LOCK, + SHAPE_KEY_UNLOCK, +}; + +static int shape_key_lock_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + const int action = RNA_enum_get(op->ptr, "action"); + const Key *keys = BKE_key_from_object(ob); + + if (!keys || BLI_listbase_is_empty(&keys->block)) { + return OPERATOR_CANCELLED; + } + + LISTBASE_FOREACH (KeyBlock *, kb, &keys->block) { + switch (action) { + case SHAPE_KEY_LOCK: + kb->flag |= KEYBLOCK_LOCKED_SHAPE; + break; + case SHAPE_KEY_UNLOCK: + kb->flag &= ~KEYBLOCK_LOCKED_SHAPE; + break; + default: + BLI_assert(0); + } + } + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + + return OPERATOR_FINISHED; +} + +static std::string shape_key_lock_description(bContext * /*C*/, + wmOperatorType * /*op*/, + PointerRNA *params) +{ + const int action = RNA_enum_get(params, "action"); + + switch (action) { + case SHAPE_KEY_LOCK: + return TIP_("Lock all shape keys of the active object"); + break; + case SHAPE_KEY_UNLOCK: + return TIP_("Unlock all shape keys of the active object"); + break; + default: + return ""; + } +} + +void OBJECT_OT_shape_key_lock(wmOperatorType *ot) +{ + static const EnumPropertyItem shape_key_lock_actions[] = { + {SHAPE_KEY_LOCK, "LOCK", 0, "Lock", "Lock all shape keys"}, + {SHAPE_KEY_UNLOCK, "UNLOCK", 0, "Unlock", "Unlock all shape keys"}, + {0, nullptr, 0, nullptr, nullptr}, + }; + + /* identifiers */ + ot->name = "Change the Lock On Shape Keys"; + ot->idname = "OBJECT_OT_shape_key_lock"; + ot->description = "Change the lock state of all shape keys of active object"; + + /* api callbacks */ + ot->poll = shape_key_exists_poll; + ot->exec = shape_key_lock_exec; + ot->get_description = shape_key_lock_description; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, + "action", + shape_key_lock_actions, + SHAPE_KEY_LOCK, + "Action", + "Lock action to execute on vertex groups"); +} + +/** \} */ diff --git a/source/blender/editors/object/object_warp.cc b/source/blender/editors/object/object_warp.cc index 297e8a01a0b..e8083b17f2f 100644 --- a/source/blender/editors/object/object_warp.cc +++ b/source/blender/editors/object/object_warp.cc @@ -22,6 +22,7 @@ #include "WM_api.hh" #include "WM_types.hh" +#include "ED_object.hh" #include "ED_transverts.hh" #include "object_intern.h" @@ -168,6 +169,10 @@ static int object_warp_verts_exec(bContext *C, wmOperator *op) float min, max; + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + return OPERATOR_CANCELLED; + } + ED_transverts_create_from_obedit(&tvs, obedit, TM_ALL_JOINTS | TM_SKIP_HANDLES); if (tvs.transverts == nullptr) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/sculpt_paint/sculpt.cc b/source/blender/editors/sculpt_paint/sculpt.cc index bfebfc4c8de..22d5e8e3fc1 100644 --- a/source/blender/editors/sculpt_paint/sculpt.cc +++ b/source/blender/editors/sculpt_paint/sculpt.cc @@ -102,6 +102,22 @@ static float sculpt_calc_radius(ViewContext *vc, } } +bool ED_sculpt_report_if_shape_key_is_locked(const Object *ob, ReportList *reports) +{ + SculptSession *ss = ob->sculpt; + + BLI_assert(ss); + + if (ss->shapekey_active && (ss->shapekey_active->flag & KEYBLOCK_LOCKED_SHAPE) != 0) { + if (reports) { + BKE_reportf(reports, RPT_ERROR, "The active shape key of %s is locked", ob->id.name + 2); + } + return true; + } + + return false; +} + /* -------------------------------------------------------------------- */ /** \name Sculpt PBVH Abstraction API * @@ -5648,6 +5664,11 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob); BKE_sculpt_mask_layers_ensure(CTX_data_depsgraph_pointer(C), CTX_data_main(C), ob, mmd); } + if (!SCULPT_tool_is_attribute_only(brush->sculpt_tool) && + ED_sculpt_report_if_shape_key_is_locked(ob, op->reports)) + { + return OPERATOR_CANCELLED; + } stroke = paint_stroke_new(C, op, diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.cc b/source/blender/editors/sculpt_paint/sculpt_cloth.cc index 675ded70075..8640538e620 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.cc +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.cc @@ -36,6 +36,8 @@ #include "WM_api.hh" #include "WM_types.hh" +#include "ED_sculpt.hh" + #include "sculpt_intern.hh" #include "RNA_access.hh" @@ -1538,6 +1540,10 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent /* Needs mask data to be available as it is used when solving the constraints. */ BKE_sculpt_update_object_for_edit(depsgraph, ob, false); + if (ED_sculpt_report_if_shape_key_is_locked(ob, op->reports)) { + return OPERATOR_CANCELLED; + } + SCULPT_stroke_id_next(ob); undo::push_begin(ob, op); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc index d37e4d37b48..456b8a961dd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.cc @@ -34,6 +34,7 @@ #include "WM_types.hh" #include "ED_screen.hh" +#include "ED_sculpt.hh" #include "ED_util.hh" #include "ED_view3d.hh" @@ -967,6 +968,11 @@ static int sculpt_mesh_filter_start(bContext *C, wmOperator *op) const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type) || use_automasking; BKE_sculpt_update_object_for_edit(depsgraph, ob, false); + + if (ED_sculpt_report_if_shape_key_is_locked(ob, op->reports)) { + return OPERATOR_CANCELLED; + } + SculptSession *ss = ob->sculpt; const eMeshFilterDeformAxis deform_axis = eMeshFilterDeformAxis( diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.hh b/source/blender/editors/sculpt_paint/sculpt_intern.hh index 1aaeccb2c51..23f6f789f15 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.hh +++ b/source/blender/editors/sculpt_paint/sculpt_intern.hh @@ -1859,6 +1859,12 @@ inline bool SCULPT_tool_is_mask(int tool) return ELEM(tool, SCULPT_TOOL_MASK); } +BLI_INLINE bool SCULPT_tool_is_attribute_only(int tool) +{ + return SCULPT_tool_is_paint(tool) || SCULPT_tool_is_mask(tool) || + ELEM(tool, SCULPT_TOOL_DRAW_FACE_SETS); +} + void SCULPT_stroke_id_ensure(Object *ob); void SCULPT_stroke_id_next(Object *ob); diff --git a/source/blender/editors/space_view3d/view3d_snap.cc b/source/blender/editors/space_view3d/view3d_snap.cc index 1c2d3abdb2c..0e272644570 100644 --- a/source/blender/editors/space_view3d/view3d_snap.cc +++ b/source/blender/editors/space_view3d/view3d_snap.cc @@ -62,7 +62,7 @@ static bool snap_calc_active_center(bContext *C, const bool select_only, float r * \{ */ /** Snaps every individual object center to its nearest point on the grid. */ -static int snap_sel_to_grid_exec(bContext *C, wmOperator * /*op*/) +static int snap_sel_to_grid_exec(bContext *C, wmOperator *op) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); @@ -93,6 +93,10 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator * /*op*/) } } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + if (ED_transverts_check_obedit(obedit)) { ED_transverts_create_from_obedit(&tvs, obedit, 0); } @@ -298,6 +302,7 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) * or if every object origin should be snapped to the given location. */ static bool snap_selected_to_location(bContext *C, + wmOperator *op, const float snap_target_global[3], const bool use_offset, const int pivot_point, @@ -341,6 +346,10 @@ static bool snap_selected_to_location(bContext *C, } } + if (ED_object_edit_report_if_shape_key_is_locked(obedit, op->reports)) { + continue; + } + if (ED_transverts_check_obedit(obedit)) { ED_transverts_create_from_obedit(&tvs, obedit, 0); } @@ -568,6 +577,7 @@ static bool snap_selected_to_location(bContext *C, } bool ED_view3d_snap_selected_to_location(bContext *C, + wmOperator *op, const float snap_target_global[3], const int pivot_point) { @@ -578,7 +588,7 @@ bool ED_view3d_snap_selected_to_location(bContext *C, * so this can be used as a low level function. */ const bool use_toolsettings = false; return snap_selected_to_location( - C, snap_target_global, use_offset, pivot_point, use_toolsettings); + C, op, snap_target_global, use_offset, pivot_point, use_toolsettings); } /** \} */ @@ -596,7 +606,7 @@ static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) const float *snap_target_global = scene->cursor.location; const int pivot_point = scene->toolsettings->transform_pivot_point; - if (snap_selected_to_location(C, snap_target_global, use_offset, pivot_point, true)) { + if (snap_selected_to_location(C, op, snap_target_global, use_offset, pivot_point, true)) { return OPERATOR_FINISHED; } return OPERATOR_CANCELLED; @@ -640,7 +650,7 @@ static int snap_selected_to_active_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (!snap_selected_to_location(C, snap_target_global, false, -1, true)) { + if (!snap_selected_to_location(C, op, snap_target_global, false, -1, true)) { return OPERATOR_CANCELLED; } return OPERATOR_FINISHED; diff --git a/source/blender/editors/transform/transform_convert_curve.cc b/source/blender/editors/transform/transform_convert_curve.cc index 096ae8560cb..cf7f68d4925 100644 --- a/source/blender/editors/transform/transform_convert_curve.cc +++ b/source/blender/editors/transform/transform_convert_curve.cc @@ -18,6 +18,8 @@ #include "BKE_context.hh" #include "BKE_curve.hh" +#include "ED_object.hh" + #include "transform.hh" #include "transform_snap.hh" @@ -86,6 +88,11 @@ static void createTransCurveVerts(bContext * /*C*/, TransInfo *t) int count = 0, countsel = 0; int count_pt = 0, countsel_pt = 0; + /* Avoid editing locked shapes. */ + if (t->mode != TFM_DUMMY && ED_object_edit_report_if_shape_key_is_locked(tc->obedit, t->reports)) { + continue; + } + /* count total of vertices, check identical as in 2nd loop for making transdata! */ ListBase *nurbs = BKE_curve_editNurbs_get(cu); LISTBASE_FOREACH (Nurb *, nu, nurbs) { diff --git a/source/blender/editors/transform/transform_convert_lattice.cc b/source/blender/editors/transform/transform_convert_lattice.cc index e6c56434c43..23c817af09d 100644 --- a/source/blender/editors/transform/transform_convert_lattice.cc +++ b/source/blender/editors/transform/transform_convert_lattice.cc @@ -17,6 +17,8 @@ #include "BKE_context.hh" #include "BKE_lattice.hh" +#include "ED_object.hh" + #include "transform.hh" #include "transform_snap.hh" @@ -40,6 +42,11 @@ static void createTransLatticeVerts(bContext * /*C*/, TransInfo *t) const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; + /* Avoid editing locked shapes. */ + if (t->mode != TFM_DUMMY && ED_object_edit_report_if_shape_key_is_locked(tc->obedit, t->reports)) { + continue; + } + bp = latt->def; a = latt->pntsu * latt->pntsv * latt->pntsw; while (a--) { diff --git a/source/blender/editors/transform/transform_convert_mesh.cc b/source/blender/editors/transform/transform_convert_mesh.cc index c3821e124a7..f9a637f1fab 100644 --- a/source/blender/editors/transform/transform_convert_mesh.cc +++ b/source/blender/editors/transform/transform_convert_mesh.cc @@ -27,6 +27,7 @@ #include "BKE_scene.h" #include "ED_mesh.hh" +#include "ED_object.hh" #include "DEG_depsgraph_query.hh" @@ -1501,6 +1502,11 @@ static void createTransEditVerts(bContext * /*C*/, TransInfo *t) TransMirrorData mirror_data = {nullptr}; TransMeshDataCrazySpace crazyspace_data = {}; + /* Avoid editing locked shapes. */ + if (t->mode != TFM_DUMMY && ED_object_edit_report_if_shape_key_is_locked(tc->obedit, t->reports)) { + continue; + } + /** * Quick check if we can transform. * diff --git a/source/blender/editors/transform/transform_convert_sculpt.cc b/source/blender/editors/transform/transform_convert_sculpt.cc index 404de477b80..5d57902e5bd 100644 --- a/source/blender/editors/transform/transform_convert_sculpt.cc +++ b/source/blender/editors/transform/transform_convert_sculpt.cc @@ -41,6 +41,11 @@ static void createTransSculpt(bContext *C, TransInfo *t) Object *ob = BKE_view_layer_active_object_get(t->view_layer); SculptSession *ss = ob->sculpt; + /* Avoid editing locked shapes. */ + if (t->mode != TFM_DUMMY && ED_sculpt_report_if_shape_key_is_locked(ob, t->reports)) { + return; + } + { BLI_assert(t->data_container_len == 1); TransDataContainer *tc = t->data_container; diff --git a/source/blender/makesdna/DNA_key_types.h b/source/blender/makesdna/DNA_key_types.h index 047a5d49544..180e9da272a 100644 --- a/source/blender/makesdna/DNA_key_types.h +++ b/source/blender/makesdna/DNA_key_types.h @@ -131,6 +131,7 @@ enum { KEYBLOCK_MUTE = (1 << 0), KEYBLOCK_SEL = (1 << 1), KEYBLOCK_LOCKED = (1 << 2), + KEYBLOCK_LOCKED_SHAPE = (1 << 3), }; #define KEYELEM_FLOAT_LEN_COORD 3 diff --git a/source/blender/makesrna/intern/rna_key.cc b/source/blender/makesrna/intern/rna_key.cc index 32cec593c60..4d12da264cd 100644 --- a/source/blender/makesrna/intern/rna_key.cc +++ b/source/blender/makesrna/intern/rna_key.cc @@ -953,6 +953,13 @@ static void rna_def_keyblock(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_CHECKBOX_HLT, -1); RNA_def_property_update(prop, 0, "rna_Key_update_data"); + prop = RNA_def_property(srna, "lock_shape", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", KEYBLOCK_LOCKED_SHAPE); + RNA_def_property_ui_text( + prop, "Lock Shape", "Protect the shape key from accidental sculpting and editing"); + RNA_def_property_ui_icon(prop, ICON_UNLOCKED, 1); + RNA_def_property_update(prop, 0, "rna_Key_update_data"); + prop = RNA_def_property(srna, "slider_min", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, nullptr, "slidermin"); RNA_def_property_range(prop, -10.0f, 10.0f); diff --git a/source/blender/modifiers/intern/MOD_cloth.cc b/source/blender/modifiers/intern/MOD_cloth.cc index 36d0d1e3044..646ba49e1f8 100644 --- a/source/blender/modifiers/intern/MOD_cloth.cc +++ b/source/blender/modifiers/intern/MOD_cloth.cc @@ -100,8 +100,8 @@ static void deform_verts(ModifierData *md, * Also hopefully new cloth system will arrive soon.. */ if (mesh == nullptr && clmd->sim_parms->shapekey_rest) { - KeyBlock *kb = BKE_keyblock_from_key(BKE_key_from_object(ctx->object), - clmd->sim_parms->shapekey_rest); + KeyBlock *kb = BKE_keyblock_find_by_index(BKE_key_from_object(ctx->object), + clmd->sim_parms->shapekey_rest); if (kb && kb->data != nullptr) { float(*layerorco)[3]; if (!(layerorco = static_cast(