diff --git a/scripts/startup/bl_ui/properties_data_mesh.py b/scripts/startup/bl_ui/properties_data_mesh.py index 7713051eb67..0ad7cfb0d5f 100644 --- a/scripts/startup/bl_ui/properties_data_mesh.py +++ b/scripts/startup/bl_ui/properties_data_mesh.py @@ -66,6 +66,7 @@ class MESH_MT_shape_key_context_menu(Menu): layout.operator("object.shape_key_mirror", text="Mirror Shape Key (Topology)").use_topology = True layout.separator() layout.operator("object.join_shapes") + layout.operator("object.update_shapes") layout.operator("object.shape_key_transfer") layout.separator() props = layout.operator("object.shape_key_remove", icon='X', text="Delete All Shape Keys") diff --git a/source/blender/editors/include/ED_mesh.hh b/source/blender/editors/include/ED_mesh.hh index a6ec836937b..188ccd6c7cd 100644 --- a/source/blender/editors/include/ED_mesh.hh +++ b/source/blender/editors/include/ED_mesh.hh @@ -518,7 +518,9 @@ void EDBM_redo_state_free(BMBackup *backup) ATTR_NONNULL(1); /* `meshtools.cc` */ wmOperatorStatus ED_mesh_join_objects_exec(bContext *C, wmOperator *op); -wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, ReportList *reports); +wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, + bool ensure_keys_exist, + ReportList *reports); /* mirror lookup api */ diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index b4d80a6b972..daf199985d9 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -717,7 +717,9 @@ wmOperatorStatus ED_mesh_join_objects_exec(bContext *C, wmOperator *op) * Add vertex positions of selected meshes as shape keys to the active mesh. * \{ */ -wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, ReportList *reports) +wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, + const bool ensure_keys_exist, + ReportList *reports) { using namespace blender; Main *bmain = CTX_data_main(C); @@ -725,6 +727,7 @@ wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, ReportList *repor Depsgraph &depsgraph = *CTX_data_ensure_evaluated_depsgraph(C); Mesh &active_mesh = *static_cast(active_object.data); + bool found_object = false; bool found_non_equal_verts_num = false; Vector compatible_objects; CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { @@ -734,6 +737,7 @@ wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, ReportList *repor if (ob_iter->type != OB_MESH) { continue; } + found_object = true; const Mesh &mesh = *static_cast(ob_iter->data); if (mesh.verts_num != active_mesh.verts_num) { found_non_equal_verts_num = true; @@ -743,6 +747,11 @@ wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, ReportList *repor } CTX_DATA_END; + if (!found_object) { + BKE_report(reports, RPT_WARNING, "No source mesh objects selected"); + return OPERATOR_CANCELLED; + } + if (found_non_equal_verts_num) { BKE_report(reports, RPT_WARNING, "Selected meshes must have equal numbers of vertices"); return OPERATOR_CANCELLED; @@ -762,6 +771,7 @@ wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, ReportList *repor &active_mesh, active_mesh.key, BKE_keyblock_add(active_mesh.key, nullptr)); } + int keys_changed = 0; Scene *scene_eval = DEG_get_evaluated_scene(&depsgraph); for (Object *object : compatible_objects) { Object *object_eval = DEG_get_evaluated_object(&depsgraph, object); @@ -770,8 +780,23 @@ wmOperatorStatus ED_mesh_shapes_join_objects_exec(bContext *C, ReportList *repor if (!deformed_mesh) { continue; } - KeyBlock *kb = BKE_keyblock_add(active_mesh.key, object->id.name + 2); - BKE_keyblock_convert_from_mesh(deformed_mesh, active_mesh.key, kb); + const char *name = BKE_id_name(object->id); + if (ensure_keys_exist) { + KeyBlock *kb = BKE_keyblock_add(active_mesh.key, name); + BKE_keyblock_convert_from_mesh(deformed_mesh, active_mesh.key, kb); + } + else if (KeyBlock *kb = BKE_keyblock_find_name(active_mesh.key, name)) { + keys_changed++; + BKE_keyblock_update_from_mesh(deformed_mesh, kb); + } + } + + if (!ensure_keys_exist) { + if (keys_changed == 0) { + BKE_report(reports, RPT_ERROR, "No name matches between selected objects and shape keys"); + return OPERATOR_CANCELLED; + } + BKE_reportf(reports, RPT_INFO, "Updated %d shape key(s)", keys_changed); } DEG_id_tag_update(&active_mesh.id, ID_RECALC_GEOMETRY); diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 0ba4afc23c9..88cfb4b9fc7 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -4847,66 +4847,70 @@ void OBJECT_OT_join(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Join as Shape Key Operator +/** \name Join Key Data Operators * \{ */ -static bool join_shapes_poll(bContext *C) +static bool active_shape_key_editable_poll(bContext *C) { Object *ob = CTX_data_active_object(C); - - if (ob == nullptr || ob->data == nullptr || !ID_IS_EDITABLE(ob) || ID_IS_OVERRIDE_LIBRARY(ob) || - ID_IS_OVERRIDE_LIBRARY(ob->data)) - { + if (!ob) { + return false; + } + if (ob->type != OB_MESH) { return false; } - /* only meshes supported at the moment */ - if (ob->type == OB_MESH) { - return ED_operator_screenactive(C); + if (ob->mode & OB_MODE_EDIT) { + CTX_wm_operator_poll_msg_set(C, "This operation is not supported in edit mode"); + return false; } - return false; + if (BKE_object_obdata_is_libdata(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit external library data"); + return false; + } + Main &bmain = *CTX_data_main(C); + if (!BKE_lib_override_library_id_is_user_deletable(&bmain, &ob->id)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit object used by override collections"); + return false; + } + return true; } static wmOperatorStatus join_shapes_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - - if (ob->mode & OB_MODE_EDIT) { - BKE_report(op->reports, RPT_ERROR, "This data does not support joining in edit mode"); - return OPERATOR_CANCELLED; - } - if (BKE_object_obdata_is_libdata(ob)) { - BKE_report(op->reports, RPT_ERROR, "Cannot edit external library data"); - return OPERATOR_CANCELLED; - } - if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) { - BKE_reportf(op->reports, - RPT_WARNING, - "Cannot edit object '%s' as it is used by override collections", - ob->id.name + 2); - return OPERATOR_CANCELLED; - } - - if (ob->type == OB_MESH) { - return ED_mesh_shapes_join_objects_exec(C, op->reports); - } - - return OPERATOR_CANCELLED; + return ED_mesh_shapes_join_objects_exec(C, true, op->reports); } void OBJECT_OT_join_shapes(wmOperatorType *ot) { - /* identifiers */ ot->name = "Join as Shapes"; - ot->description = "Copy the current resulting shape of another selected object to this one"; + ot->description = + "Add the vertex positions of selected objects as shape keys or update existing shape keys " + "with matching names"; ot->idname = "OBJECT_OT_join_shapes"; - /* api callbacks */ ot->exec = join_shapes_exec; - ot->poll = join_shapes_poll; + ot->poll = active_shape_key_editable_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static wmOperatorStatus update_all_shape_keys_exec(bContext *C, wmOperator *op) +{ + return ED_mesh_shapes_join_objects_exec(C, false, op->reports); +} + +void OBJECT_OT_update_shapes(wmOperatorType *ot) +{ + ot->name = "Update from Objects"; + ot->description = + "Update existing shape keys with the vertex positions of selected objects with matching " + "names"; + ot->idname = "OBJECT_OT_update_shapes"; + + ot->exec = update_all_shape_keys_exec; + ot->poll = active_shape_key_editable_poll; - /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } diff --git a/source/blender/editors/object/object_intern.hh b/source/blender/editors/object/object_intern.hh index 12039f85a72..94b39a63cf7 100644 --- a/source/blender/editors/object/object_intern.hh +++ b/source/blender/editors/object/object_intern.hh @@ -141,6 +141,7 @@ void OBJECT_OT_duplicate(wmOperatorType *ot); void OBJECT_OT_delete(wmOperatorType *ot); void OBJECT_OT_join(wmOperatorType *ot); void OBJECT_OT_join_shapes(wmOperatorType *ot); +void OBJECT_OT_update_shapes(wmOperatorType *ot); void OBJECT_OT_convert(wmOperatorType *ot); /* `object_volume.cc` */ diff --git a/source/blender/editors/object/object_ops.cc b/source/blender/editors/object/object_ops.cc index 5ef09bec458..6e8dc50a43b 100644 --- a/source/blender/editors/object/object_ops.cc +++ b/source/blender/editors/object/object_ops.cc @@ -112,6 +112,7 @@ void operatortypes_object() WM_operatortype_append(OBJECT_OT_duplicate); WM_operatortype_append(OBJECT_OT_join); WM_operatortype_append(OBJECT_OT_join_shapes); + WM_operatortype_append(OBJECT_OT_update_shapes); WM_operatortype_append(OBJECT_OT_convert); WM_operatortype_append(OBJECT_OT_visual_geometry_to_objects);