Shape Keys: Add operator to update keys from selected objects
This operator is very similar to the existing "Join as Shapes" operator, which updates or adds shape key array values with the positions of objects with the same name, but instead of creating new shape keys, it just updates existing ones. The new operator is called "Update from Objects" in the UI. Internally, some logic was moved to a poll function shared between the two operators, and a new argument for whether to ensure keys exist was added to their shared implementation. Part of #135095. Pull Request: https://projects.blender.org/blender/blender/pulls/136853
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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<Mesh *>(active_object.data);
|
||||
|
||||
bool found_object = false;
|
||||
bool found_non_equal_verts_num = false;
|
||||
Vector<Object *> 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<Mesh *>(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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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` */
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user