diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index b7e02f06571..0da29ded13d 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2982,6 +2982,16 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ if (VALID_CONS_TARGET(ct) || data->flag & ACTCON_USE_EVAL_TIME) { switch (data->mix_mode) { + /* Simple matrix multiplication. */ + case ACTCON_MIX_BEFORE_FULL: + mul_m4_m4m4(cob->matrix, ct->matrix, cob->matrix); + break; + + case ACTCON_MIX_AFTER_FULL: + mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + break; + + /* Aligned Inherit Scale emulation. */ case ACTCON_MIX_BEFORE: mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix); break; @@ -2990,8 +3000,13 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix); break; - case ACTCON_MIX_AFTER_FULL: - mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + /* Fully separate handling of channels. */ + case ACTCON_MIX_BEFORE_SPLIT: + mul_m4_m4m4_split_channels(cob->matrix, ct->matrix, cob->matrix); + break; + + case ACTCON_MIX_AFTER_SPLIT: + mul_m4_m4m4_split_channels(cob->matrix, cob->matrix, ct->matrix); break; default: @@ -5767,6 +5782,17 @@ static bConstraint *add_new_constraint(Object *ob, } break; } + case CONSTRAINT_TYPE_ACTION: { + /* The Before or Split modes require computing in local space, but + * for objects the Local space doesn't make sense (T78462, D6095 etc). + * So only default to Before (Split) if the constraint is on a bone. */ + if (pchan) { + bActionConstraint *data = con->data; + data->mix_mode = ACTCON_MIX_BEFORE_SPLIT; + con->ownspace = CONSTRAINT_SPACE_LOCAL; + } + break; + } } return con; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 00fd008151d..e77fedfe143 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -861,10 +861,13 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list) /* The Action constraint only does this in the Before mode. */ bActionConstraint *data = (bActionConstraint *)con->data; - if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE) && + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE, ACTCON_MIX_BEFORE_FULL) && ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) { return true; } + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) { + return true; + } } else if (con->type == CONSTRAINT_TYPE_TRANSFORM) { /* Transform constraint needs it for rotation at least (r.57309), diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index a77fbc9e45e..822b8705c9b 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -901,10 +901,16 @@ typedef enum eActionConstraint_Flags { typedef enum eActionConstraint_MixMode { /* Multiply the action transformation on the right. */ ACTCON_MIX_AFTER_FULL = 0, + /* Multiply the action transformation on the left. */ + ACTCON_MIX_BEFORE_FULL = 3, /* Multiply the action transformation on the right, with anti-shear scale handling. */ ACTCON_MIX_AFTER = 1, /* Multiply the action transformation on the left, with anti-shear scale handling. */ ACTCON_MIX_BEFORE = 2, + /* Separately combine Translation, Rotation and Scale, with rotation on the right. */ + ACTCON_MIX_AFTER_SPLIT = 4, + /* Separately combine Translation, Rotation and Scale, with rotation on the left. */ + ACTCON_MIX_BEFORE_SPLIT = 5, } eActionConstraint_MixMode; /* Locked-Axis Values (Locked Track) */ diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index a0a0a41b58d..e36d052d27c 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -679,7 +679,7 @@ static void rna_ActionConstraint_mix_mode_set(PointerRNA *ptr, int value) acon->mix_mode = value; /* The After mode can be computed in world space for efficiency - * and backward compatibility, while Before requires Local. */ + * and backward compatibility, while Before or Split requires Local. */ if (ELEM(value, ACTCON_MIX_AFTER, ACTCON_MIX_AFTER_FULL)) { con->ownspace = CONSTRAINT_SPACE_WORLD; } @@ -1773,25 +1773,47 @@ static void rna_def_constraint_action(BlenderRNA *brna) }; static const EnumPropertyItem mix_mode_items[] = { + {ACTCON_MIX_BEFORE_FULL, + "BEFORE_FULL", + 0, + "Before Original (Full)", + "Apply the action channels before the original transformation, as if applied to an " + "imaginary parent in Full Inherit Scale mode. Will create shear when combining rotation " + "and non-uniform scale"}, {ACTCON_MIX_BEFORE, "BEFORE", 0, - "Before Original", - "Apply the action channels before the original transformation, " - "as if applied to an imaginary parent with Aligned Inherit Scale"}, - {ACTCON_MIX_AFTER, - "AFTER", + "Before Original (Aligned)", + "Apply the action channels before the original transformation, as if applied to an " + "imaginary parent in Aligned Inherit Scale mode. This effectively uses Full for location " + "and Split Channels for rotation and scale"}, + {ACTCON_MIX_BEFORE_SPLIT, + "BEFORE_SPLIT", 0, - "After Original", - "Apply the action channels after the original transformation, " - "as if applied to an imaginary child with Aligned Inherit Scale"}, + "Before Original (Split Channels)", + "Apply the action channels before the original transformation, handling location, rotation " + "and scale separately"}, + {0, "", 0, NULL, NULL}, {ACTCON_MIX_AFTER_FULL, "AFTER_FULL", 0, - "After Original (Full Scale)", - "Apply the action channels after the original transformation, as if " - "applied to an imaginary child with Full Inherit Scale. This mode " - "can create shear and is provided only for backward compatibility"}, + "After Original (Full)", + "Apply the action channels after the original transformation, as if applied to an " + "imaginary child in Full Inherit Scale mode. Will create shear when combining rotation " + "and non-uniform scale"}, + {ACTCON_MIX_AFTER, + "AFTER", + 0, + "After Original (Aligned)", + "Apply the action channels after the original transformation, as if applied to an " + "imaginary child in Aligned Inherit Scale mode. This effectively uses Full for location " + "and Split Channels for rotation and scale"}, + {ACTCON_MIX_AFTER_SPLIT, + "AFTER_SPLIT", + 0, + "After Original (Split Channels)", + "Apply the action channels after the original transformation, handling location, rotation " + "and scale separately"}, {0, NULL, 0, NULL, NULL}, };