[Bf-blender-cvs] [8621fdb10dc] master: Apply Object Transform: Multi-user data support

Dalai Felinto noreply at git.blender.org
Wed Mar 30 11:12:01 CEST 2022


Commit: 8621fdb10dc402eeff5aa996eeb992a513afd4c0
Author: Dalai Felinto
Date:   Wed Mar 30 11:07:29 2022 +0200
Branches: master
https://developer.blender.org/rB8621fdb10dc402eeff5aa996eeb992a513afd4c0

Apply Object Transform: Multi-user data support

The current behaviour is to prevent multi-user data from having its
transformation applied.

However in some particular cases it is possible to apply them:
* If all the users of the multi-user data are part of the selection.
* If not all the users are in the selection but the selection is made
single-user.

The active object is used as reference to set the transformation of the
other selected objects.

Note: For simplicity sake, this new behaviour is only available if all
the selection is using the same data.

Differential Revision: https://developer.blender.org/D14377

===================================================================

M	release/scripts/startup/bl_ui/space_view3d.py
M	source/blender/editors/object/object_transform.cc

===================================================================

diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index e78ea9d7fc1..0919faa8460 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2640,6 +2640,8 @@ class VIEW3D_MT_object_apply(Menu):
 
     def draw(self, _context):
         layout = self.layout
+        # Need invoke for the popup confirming the multi-user data operation
+        layout.operator_context = 'INVOKE_DEFAULT'
 
         props = layout.operator("object.transform_apply", text="Location", text_ctxt=i18n_contexts.default)
         props.location, props.rotation, props.scale = True, False, False
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index da75703a0d9..e279ebbb02e 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -587,18 +587,99 @@ static Array<Object *> sorted_selected_editable_objects(bContext *C)
   return sorted_objects;
 }
 
+/**
+ * Check if we need and can handle the special multiuser case.
+ */
+static bool apply_objects_internal_can_multiuser(bContext *C)
+{
+  Object *obact = CTX_data_active_object(C);
+
+  if (ELEM(NULL, obact, obact->data)) {
+    return false;
+  }
+
+  if (ID_REAL_USERS(obact->data) == 1) {
+    return false;
+  }
+
+  bool all_objects_same_data = true;
+  bool obact_selected = false;
+
+  CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
+    if (ob->data != obact->data) {
+      all_objects_same_data = false;
+      break;
+    }
+
+    if (ob == obact) {
+      obact_selected = true;
+    }
+  }
+  CTX_DATA_END;
+
+  return all_objects_same_data && obact_selected;
+}
+
+/**
+ * Check if the current selection need to be made into single user
+ *
+ * It assumes that all selected objects share the same object data.
+ */
+static bool apply_objects_internal_need_single_user(bContext *C)
+{
+  Object *ob = CTX_data_active_object(C);
+  BLI_assert(apply_objects_internal_can_multiuser(C));
+
+  /* Counting the number of objects is valid since it's known the
+   * selection is only made up of users of the active objects data. */
+  return (ID_REAL_USERS(ob->data) > CTX_DATA_COUNT(C, selected_editable_objects));
+}
+
 static int apply_objects_internal(bContext *C,
                                   ReportList *reports,
                                   bool apply_loc,
                                   bool apply_rot,
                                   bool apply_scale,
-                                  bool do_props)
+                                  bool do_props,
+                                  bool do_single_user)
 {
   Main *bmain = CTX_data_main(C);
   Scene *scene = CTX_data_scene(C);
   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
   float rsmat[3][3], obmat[3][3], iobmat[3][3], mat[4][4], scale;
   bool changed = true;
+  bool do_multi_user = apply_objects_internal_can_multiuser(C);
+  float obact_invmat[4][4], obact_parent[4][4], obact_parentinv[4][4];
+
+  /* Only used when do_multi_user is set .*/
+  Object *obact = NULL;
+  bool make_single_user = false;
+
+  if (do_multi_user) {
+    obact = CTX_data_active_object(C);
+    invert_m4_m4(obact_invmat, obact->obmat);
+
+    Object workob;
+    BKE_object_workob_calc_parent(depsgraph, scene, obact, &workob);
+    copy_m4_m4(obact_parent, workob.obmat);
+    copy_m4_m4(obact_parentinv, obact->parentinv);
+
+    if (apply_objects_internal_need_single_user(C)) {
+      if (do_single_user) {
+        make_single_user = true;
+      }
+      else {
+        ID *obact_data = static_cast<ID *>(obact->data);
+        BKE_reportf(reports,
+                    RPT_ERROR,
+                    "Cannot apply to a multi user: Object \"%s\", %s \"%s\", aborting",
+                    obact->id.name + 2,
+                    BKE_idtype_idcode_to_name(GS(obact_data->name)),
+                    obact_data->name + 2);
+        return OPERATOR_CANCELLED;
+      }
+    }
+  }
 
   /* first check if we can execute */
   CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
@@ -612,7 +693,7 @@ static int apply_objects_internal(bContext *C,
              OB_FONT,
              OB_GPENCIL)) {
       ID *obdata = static_cast<ID *>(ob->data);
-      if (ID_REAL_USERS(obdata) > 1) {
+      if (!do_multi_user && ID_REAL_USERS(obdata) > 1) {
         BKE_reportf(reports,
                     RPT_ERROR,
                     R"(Cannot apply to a multi user: Object "%s", %s "%s", aborting)",
@@ -728,6 +809,15 @@ static int apply_objects_internal(bContext *C,
   changed = false;
 
   /* now execute */
+
+  if (make_single_user) {
+    /* Make single user. */
+    ED_object_single_obdata_user(bmain, scene, obact);
+    BKE_main_id_newptr_and_tag_clear(bmain);
+    WM_event_add_notifier(C, NC_WINDOW, NULL);
+    DEG_relations_tag_update(bmain);
+  }
+
   Array<Object *> objects = sorted_selected_editable_objects(C);
   if (objects.is_empty()) {
     return OPERATOR_CANCELLED;
@@ -774,7 +864,14 @@ static int apply_objects_internal(bContext *C,
     }
 
     /* apply to object data */
-    if (ob->type == OB_MESH) {
+    if (do_multi_user && ob != obact) {
+      /* Don't apply, just set the new object data, the correct
+       * transformations will happen later. */
+      id_us_min((ID *)ob->data);
+      ob->data = obact->data;
+      id_us_plus((ID *)ob->data);
+    }
+    else if (ob->type == OB_MESH) {
       Mesh *me = static_cast<Mesh *>(ob->data);
 
       if (apply_scale) {
@@ -882,16 +979,53 @@ static int apply_objects_internal(bContext *C,
       continue;
     }
 
-    if (apply_loc) {
-      zero_v3(ob->loc);
-    }
-    if (apply_scale) {
-      ob->scale[0] = ob->scale[1] = ob->scale[2] = 1.0f;
+    if (do_multi_user && ob != obact) {
+      float _obmat[4][4], _iobmat[4][4];
+      float _mat[4][4];
+
+      copy_m4_m4(_obmat, ob->obmat);
+      invert_m4_m4(_iobmat, _obmat);
+
+      copy_m4_m4(_mat, _obmat);
+      mul_m4_m4_post(_mat, obact_invmat);
+      mul_m4_m4_post(_mat, obact_parent);
+      mul_m4_m4_post(_mat, obact_parentinv);
+
+      if (apply_loc && apply_scale && apply_rot) {
+        BKE_object_apply_mat4(ob, _mat, false, true);
+      }
+      else {
+        Object ob_temp = blender::dna::shallow_copy(*ob);
+        BKE_object_apply_mat4(&ob_temp, _mat, false, true);
+
+        if (apply_loc) {
+          copy_v3_v3(ob->loc, ob_temp.loc);
+        }
+
+        if (apply_scale) {
+          copy_v3_v3(ob->scale, ob_temp.scale);
+        }
+
+        if (apply_rot) {
+          copy_v4_v4(ob->quat, ob_temp.quat);
+          copy_v3_v3(ob->rot, ob_temp.rot);
+          copy_v3_v3(ob->rotAxis, ob_temp.rotAxis);
+          ob->rotAngle = ob_temp.rotAngle;
+        }
+      }
     }
-    if (apply_rot) {
-      zero_v3(ob->rot);
-      unit_qt(ob->quat);
-      unit_axis_angle(ob->rotAxis, &ob->rotAngle);
+    else {
+      if (apply_loc) {
+        zero_v3(ob->loc);
+      }
+      if (apply_scale) {
+        ob->scale[0] = ob->scale[1] = ob->scale[2] = 1.0f;
+      }
+      if (apply_rot) {
+        zero_v3(ob->rot);
+        unit_qt(ob->quat);
+        unit_axis_angle(ob->rotAxis, &ob->rotAngle);
+      }
     }
 
     Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
@@ -969,14 +1103,35 @@ static int object_transform_apply_exec(bContext *C, wmOperator *op)
   const bool rot = RNA_boolean_get(op->ptr, "rotation");
   const bool sca = RNA_boolean_get(op->ptr, "scale");
   const bool do_props = RNA_boolean_get(op->ptr, "properties");
+  const bool do_single_user = RNA_boolean_get(op->ptr, "isolate_users");
 
   if (loc || rot || sca) {
-    return apply_objects_internal(C, op->reports, loc, rot, sca, do_props);
+    return apply_objects_internal(C, op->reports, loc, rot, sca, do_props, do_single_user);
   }
   /* allow for redo */
   return OPERATOR_FINISHED;
 }
 
+static int object_transform_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+  Object *ob = ED_object_active_context(C);
+
+  bool can_handle_multiuser = apply_objects_internal_can_multiuser(C);
+  bool need_single_user = can_handle_multiuser && apply_objects_internal_need_single_user(C);
+
+  if ((ob->data != NULL) && need_single_user) {
+    PropertyRNA *prop = RNA_struct_find_property(op->ptr, "isolate_users");
+    if (!RNA_property_is_set(op->ptr, prop)) {
+      RNA_property_boolean_set(op->ptr, prop, true);
+    }
+    if (RNA_property_boolean_get(op->ptr, prop)) {
+      return WM_operator_confirm_message(
+          C, op, "Create new object-data users and apply transformation");
+    }
+  }
+  return object_transform_apply_exec(C, op);
+}
+
 void OBJECT_OT_transform_apply(wmOperatorType *ot)
 {
   /* identifiers */
@@ -986,6 +1141,7 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot)
 
   /* api callbacks */
   ot->exec = object_transform_apply_exec;
+  ot->invoke = object_transform_apply_invoke;
   ot->poll = ED_operator_objectmode;
 
   /* flags */
@@ -999,6 +1155,13 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot)
                   true,
                   "Apply Properties",
                   "Modify properties such as curve vertex radius, font size and bone envelope");
+  PropertyRNA *prop = RNA_def_boolean(ot->srna,
+                                      "isolate_users",
+                                      false,
+                                      "Isolate Multi User Data",
+                                      "Create new object-data users if needed");
+  RNA_def_property_flag(prop, PROP_HIDDEN);
+  RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 }
 
 /** \} */



More information about the Bf-blender-cvs mailing list