diff --git a/scripts/startup/bl_ui/properties_data_mesh.py b/scripts/startup/bl_ui/properties_data_mesh.py index a9ecf9a9e3f..7713051eb67 100644 --- a/scripts/startup/bl_ui/properties_data_mesh.py +++ b/scripts/startup/bl_ui/properties_data_mesh.py @@ -60,6 +60,7 @@ class MESH_MT_shape_key_context_menu(Menu): layout = self.layout layout.operator("object.shape_key_add", icon='ADD', text="New Shape from Mix").from_mix = True + layout.operator("object.shape_key_copy", icon='DUPLICATE', text="Duplicate Shape Key") layout.separator() layout.operator("object.shape_key_mirror", icon='ARROW_LEFTRIGHT').use_topology = False layout.operator("object.shape_key_mirror", text="Mirror Shape Key (Topology)").use_topology = True diff --git a/source/blender/blenkernel/BKE_key.hh b/source/blender/blenkernel/BKE_key.hh index 6989b43c2f0..c8b24e4611c 100644 --- a/source/blender/blenkernel/BKE_key.hh +++ b/source/blender/blenkernel/BKE_key.hh @@ -81,6 +81,10 @@ KeyBlock *BKE_keyblock_from_object(Object *ob); KeyBlock *BKE_keyblock_from_object_reference(Object *ob); KeyBlock *BKE_keyblock_add(Key *key, const char *name); + +/** Add a copy of the source key-block with a copy of its data array. */ +KeyBlock *BKE_keyblock_duplicate(Key *key, const KeyBlock *kb_src); + /** * \note sorting is a problematic side effect in some cases, * better only do this explicitly by having its own function, diff --git a/source/blender/blenkernel/intern/key.cc b/source/blender/blenkernel/intern/key.cc index 933a6aea279..1788d7261c0 100644 --- a/source/blender/blenkernel/intern/key.cc +++ b/source/blender/blenkernel/intern/key.cc @@ -1878,6 +1878,16 @@ KeyBlock *BKE_keyblock_add(Key *key, const char *name) return kb; } +KeyBlock *BKE_keyblock_duplicate(Key *key, const KeyBlock *kb_src) +{ + BLI_assert(BLI_findindex(&key->block, kb_src) != -1); + KeyBlock *kb_dst = BKE_keyblock_add(key, kb_src->name); + kb_dst->totelem = kb_src->totelem; + kb_dst->data = MEM_dupallocN(kb_src->data); + BKE_keyblock_copy_settings(kb_dst, kb_src); + return kb_dst; +} + KeyBlock *BKE_keyblock_add_ctime(Key *key, const char *name, const bool do_force) { KeyBlock *kb = BKE_keyblock_add(key, name); diff --git a/source/blender/editors/object/object_intern.hh b/source/blender/editors/object/object_intern.hh index ba4c0424e90..38943001246 100644 --- a/source/blender/editors/object/object_intern.hh +++ b/source/blender/editors/object/object_intern.hh @@ -312,6 +312,7 @@ void TRANSFORM_OT_vertex_warp(wmOperatorType *ot); /* `object_shapekey.cc` */ void OBJECT_OT_shape_key_add(wmOperatorType *ot); +void OBJECT_OT_shape_key_copy(wmOperatorType *ot); void OBJECT_OT_shape_key_remove(wmOperatorType *ot); void OBJECT_OT_shape_key_clear(wmOperatorType *ot); void OBJECT_OT_shape_key_retime(wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.cc b/source/blender/editors/object/object_ops.cc index bcc4ac55cd4..9efb7d0595c 100644 --- a/source/blender/editors/object/object_ops.cc +++ b/source/blender/editors/object/object_ops.cc @@ -221,6 +221,7 @@ void operatortypes_object() WM_operatortype_append(OBJECT_OT_link_to_collection); WM_operatortype_append(OBJECT_OT_shape_key_add); + WM_operatortype_append(OBJECT_OT_shape_key_copy); WM_operatortype_append(OBJECT_OT_shape_key_remove); WM_operatortype_append(OBJECT_OT_shape_key_clear); WM_operatortype_append(OBJECT_OT_shape_key_retime); diff --git a/source/blender/editors/object/object_shapekey.cc b/source/blender/editors/object/object_shapekey.cc index 419994e9f92..246f263e4d6 100644 --- a/source/blender/editors/object/object_shapekey.cc +++ b/source/blender/editors/object/object_shapekey.cc @@ -376,6 +376,35 @@ void OBJECT_OT_shape_key_add(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Shape Key Duplicate Operator + * \{ */ + +static wmOperatorStatus shape_key_copy_exec(bContext *C, wmOperator * /*op*/) +{ + Object *ob = context_object(C); + Key *key = BKE_key_from_object(ob); + BKE_keyblock_duplicate(key, BKE_keyblock_from_object(ob)); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + DEG_relations_tag_update(CTX_data_main(C)); + return OPERATOR_FINISHED; +} + +void OBJECT_OT_shape_key_copy(wmOperatorType *ot) +{ + ot->name = "Duplicate Shape Key"; + ot->idname = "OBJECT_OT_shape_key_copy"; + ot->description = "Duplicate the acive shape key"; + + ot->poll = shape_key_mode_exists_poll; + ot->exec = shape_key_copy_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Shape Key Remove Operator * \{ */