diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object.h | 11 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.cc | 49 | ||||
-rw-r--r-- | source/blender/editors/object/object_intern.h | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_ops.c | 1 | ||||
-rw-r--r-- | source/blender/editors/object/object_relations.c | 36 | ||||
-rw-r--r-- | source/blender/editors/object/object_transform.cc | 32 |
7 files changed, 127 insertions, 6 deletions
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; +} + /** \} */ /* -------------------------------------------------------------------- */ |