[Bf-blender-cvs] [db6287873cd] master: Object: Set Parent (Keep Transform Without Inverse)

Wayde Moss noreply at git.blender.org
Fri Apr 15 02:32:47 CEST 2022


Commit: db6287873cd2c79741439f4d141c4b2db517d73e
Author: Wayde Moss
Date:   Thu Apr 14 18:16:23 2022 -0400
Branches: master
https://developer.blender.org/rBdb6287873cd2c79741439f4d141c4b2db517d73e

Object: Set Parent (Keep Transform Without Inverse)

**Relevant to Artists:** This patch adds an option to the Parenting
menu, `Object (Keep Transform Without Inverse)`, and Apply menu, `Parent
Inverse`. The operators preserve the child's world transform without
using the parent inverse matrix. Effectively, we set the child's origin
to the parent. When the child has an identity local transform, then the
child is world-space aligned with its parent (scale excluded).

**Technical:** In both cases, the hidden parent inverse matrix is
generally set to identity (cleared or "not used") as long as the parent
has no shear. If the parent has shear, then this matrix will not be
entirely cleared. It will contain shear to counter the parent's shear.
This is required, otherwise the object's local matrix cannot be properly
decomposed into location, rotation and scale, and thus cannot preserve
the world transform.

If the child's world transform has shear, then its world transform is
not preserved. This is currently not supported for consistency in the
handling of shear during the other parenting ops: Parent (Keep
Transform), Clear [Parent] and Keep Transform. If it should work, then
another patch should add the support for all of them.

Reviewed By: sybren, RiggingDojo
Differential Revision: https://developer.blender.org/D14581

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

M	release/scripts/startup/bl_ui/space_view3d.py
M	source/blender/blenkernel/BKE_object.h
M	source/blender/blenkernel/intern/object.cc
M	source/blender/editors/object/object_intern.h
M	source/blender/editors/object/object_ops.c
M	source/blender/editors/object/object_relations.c
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 ecd48aab697..74f20aca072 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2722,6 +2722,9 @@ class VIEW3D_MT_object_apply(Menu):
             text_ctxt=i18n_contexts.default,
         ).target = 'MESH'
         layout.operator("object.duplicates_make_real")
+        layout.operator("object.parent_inverse_apply",
+                        text="Parent Inverse",
+                        text_ctxt=i18n_contexts.default)
 
 
 class VIEW3D_MT_object_parent(Menu):
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index eaeb6e6a2e4..faf878dfc2a 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -270,6 +270,17 @@ void BKE_object_apply_mat4(struct Object *ob,
                            const float mat[4][4],
                            bool use_compat,
                            bool use_parent);
+
+/**
+ * Use parent's world location and rotation as the child's origin. The parent inverse will
+ * become identity when the parent has no shearing. Otherwise, it is non-identity and contains the
+ * object's local matrix data that cannot be decomposed into location, rotation and scale.
+ *
+ * Assumes the object's world matrix has no shear.
+ * Assumes parent exists.
+ */
+void BKE_object_apply_parent_inverse(struct Object *ob);
+
 void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4]);
 
 bool BKE_object_pose_context_check(const struct Object *ob);
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index 92c350c5208..5ff1f6b950f 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -3558,6 +3558,55 @@ void BKE_object_apply_mat4(Object *ob,
   BKE_object_apply_mat4_ex(ob, mat, use_parent ? ob->parent : nullptr, ob->parentinv, use_compat);
 }
 
+void BKE_object_apply_parent_inverse(struct Object *ob)
+{
+  /*
+   * Use parent's world transform as the child's origin.
+   *
+   * Let:
+   *    local = identity
+   *    world = orthonormalized(parent)
+   *
+   * Then:
+   *    world = parent @ parentinv @ local
+   *    inv(parent) @ world = parentinv
+   *    parentinv = inv(parent) @ world
+   *
+   * NOTE: If ob->obmat has shear, then this `parentinv` is insufficient because
+   *    parent @ parentinv => shearless result
+   *
+   *    Thus, local will have shear which cannot be decomposed into TRS:
+   *    local = inv(parent @ parentinv) @ world
+   *
+   *    This is currently not supported for consistency in the handling of shear during the other
+   *    parenting ops: Parent (Keep Transform), Clear [Parent] and Keep Transform.
+   */
+  float par_locrot[4][4], par_imat[4][4];
+  BKE_object_get_parent_matrix(ob, ob->parent, par_locrot);
+  invert_m4_m4(par_imat, par_locrot);
+
+  orthogonalize_m4_stable(par_locrot, 0, true);
+
+  mul_m4_m4m4(ob->parentinv, par_imat, par_locrot);
+
+  /* Now, preserve `world` given the new `parentinv`.
+   *
+   * world = parent @ parentinv @ local
+   * inv(parent) @ world = parentinv @ local
+   * inv(parentinv) @ inv(parent) @ world = local
+   *
+   * local = inv(parentinv) @ inv(parent) @ world
+   */
+  float ob_local[4][4];
+  copy_m4_m4(ob_local, ob->parentinv);
+  invert_m4(ob_local);
+  mul_m4_m4_post(ob_local, par_imat);
+  mul_m4_m4_post(ob_local, ob->obmat);
+
+  /* Send use_compat=False so the rotation is predictable. */
+  BKE_object_apply_mat4(ob, ob_local, false, false);
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index dd6e11abbf9..eae08e89104 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -36,6 +36,7 @@ void OBJECT_OT_scale_clear(struct wmOperatorType *ot);
 void OBJECT_OT_origin_clear(struct wmOperatorType *ot);
 void OBJECT_OT_visual_transform_apply(struct wmOperatorType *ot);
 void OBJECT_OT_transform_apply(struct wmOperatorType *ot);
+void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot);
 void OBJECT_OT_transform_axis_target(struct wmOperatorType *ot);
 void OBJECT_OT_origin_set(struct wmOperatorType *ot);
 
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index cf5c349228f..ad0d6b88123 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -33,6 +33,7 @@ void ED_operatortypes_object(void)
   WM_operatortype_append(OBJECT_OT_origin_clear);
   WM_operatortype_append(OBJECT_OT_visual_transform_apply);
   WM_operatortype_append(OBJECT_OT_transform_apply);
+  WM_operatortype_append(OBJECT_OT_parent_inverse_apply);
   WM_operatortype_append(OBJECT_OT_transform_axis_target);
   WM_operatortype_append(OBJECT_OT_origin_set);
 
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 7be46bdb24b..5ef8e573e27 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -936,8 +936,19 @@ static int parent_set_invoke_menu(bContext *C, wmOperatorType *ot)
   RNA_boolean_set(&opptr, "keep_transform", true);
 #endif
 
-  uiItemO(
-      layout, IFACE_("Object (Without Inverse)"), ICON_NONE, "OBJECT_OT_parent_no_inverse_set");
+  uiItemBooleanO(layout,
+                 IFACE_("Object (Without Inverse)"),
+                 ICON_NONE,
+                 "OBJECT_OT_parent_no_inverse_set",
+                 "keep_transform",
+                 0);
+
+  uiItemBooleanO(layout,
+                 IFACE_("Object (Keep Transform Without Inverse)"),
+                 ICON_NONE,
+                 "OBJECT_OT_parent_no_inverse_set",
+                 "keep_transform",
+                 1);
 
   struct {
     bool mesh, gpencil;
@@ -1055,6 +1066,8 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op)
   Main *bmain = CTX_data_main(C);
   Object *par = ED_object_active_context(C);
 
+  const bool keep_transform = RNA_boolean_get(op->ptr, "keep_transform");
+
   DEG_id_tag_update(&par->id, ID_RECALC_TRANSFORM);
 
   /* context iterator */
@@ -1064,16 +1077,21 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op)
         BKE_report(op->reports, RPT_ERROR, "Loop in parents");
       }
       else {
-        /* clear inverse matrix and also the object location */
-        unit_m4(ob->parentinv);
-        memset(ob->loc, 0, sizeof(float[3]));
-
         /* set recalc flags */
         DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
 
         /* set parenting type for object - object only... */
         ob->parent = par;
         ob->partype = PAROBJECT; /* NOTE: DNA define, not operator property. */
+
+        if (keep_transform) {
+          BKE_object_apply_parent_inverse(ob);
+          continue;
+        }
+
+        /* clear inverse matrix and also the object location */
+        unit_m4(ob->parentinv);
+        memset(ob->loc, 0, sizeof(float[3]));
       }
     }
   }
@@ -1100,6 +1118,12 @@ void OBJECT_OT_parent_no_inverse_set(wmOperatorType *ot)
 
   /* flags */
   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+  RNA_def_boolean(ot->srna,
+                  "keep_transform",
+                  false,
+                  "Keep Transform",
+                  "Preserve the world transform throughout parenting");
 }
 
 /** \} */
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index 3d4d543012d..c0ec6c6678e 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -1173,6 +1173,38 @@ void OBJECT_OT_transform_apply(wmOperatorType *ot)
   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
 }
 
+static int object_parent_inverse_apply_exec(bContext *C, wmOperator *UNUSED(op))
+{
+  CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) {
+    if (ob->parent == NULL) {
+      continue;
+    }
+
+    DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
+    BKE_object_apply_parent_inverse(ob);
+  }
+  CTX_DATA_END;
+
+  WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL);
+
+  return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Apply Parent Inverse";
+  ot->description = "Apply the object's parent inverse to the its data";
+  ot->idname = "OBJECT_OT_parent_inverse_apply";
+
+  /* api callbacks */
+  ot->exec = object_parent_inverse_apply_exec;
+  ot->poll = ED_operator_objectmode;
+
+  /* flags */
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
 /** \} */
 
 /* -------------------------------------------------------------------- */



More information about the Bf-blender-cvs mailing list