diff --git a/source/blender/blenkernel/intern/constraint.cc b/source/blender/blenkernel/intern/constraint.cc index e2d4c06a5ae..f1d45e5192e 100644 --- a/source/blender/blenkernel/intern/constraint.cc +++ b/source/blender/blenkernel/intern/constraint.cc @@ -2915,6 +2915,11 @@ 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) { + /* Replace the input transformation. */ + case ACTCON_MIX_REPLACE: + copy_m4_m4(cob->matrix, ct->matrix); + break; + /* Simple matrix multiplication. */ case ACTCON_MIX_BEFORE_FULL: mul_m4_m4m4(cob->matrix, ct->matrix, cob->matrix); diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 0bb44d77d97..86a7487d0a3 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -842,6 +842,8 @@ typedef enum eActionConstraint_Flags { /** #bActionConstraint.mix_mode */ typedef enum eActionConstraint_MixMode { + /* Replace the input transformation. */ + ACTCON_MIX_REPLACE = 6, /* Multiply the action transformation on the right. */ ACTCON_MIX_AFTER_FULL = 0, /* Multiply the action transformation on the left. */ diff --git a/source/blender/makesrna/intern/rna_constraint.cc b/source/blender/makesrna/intern/rna_constraint.cc index 663bb2b1917..4fc5ab608c8 100644 --- a/source/blender/makesrna/intern/rna_constraint.cc +++ b/source/blender/makesrna/intern/rna_constraint.cc @@ -1848,6 +1848,12 @@ static void rna_def_constraint_action(BlenderRNA *brna) }; static const EnumPropertyItem mix_mode_items[] = { + {ACTCON_MIX_REPLACE, + "REPLACE", + 0, + "Replace", + "Replace the original transformation with the action channels"}, + RNA_ENUM_ITEM_SEPR, {ACTCON_MIX_BEFORE_FULL, "BEFORE_FULL", 0, diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py index 431f00fe018..4e76d46f422 100644 --- a/tests/python/bl_constraints.py +++ b/tests/python/bl_constraints.py @@ -460,6 +460,47 @@ class ActionConstraintTest(AbstractConstraintTests): con.action_slot, "Assigning an Action with a virgin slot should automatically select that slot") + def test_mix_modes(self): + owner = bpy.context.scene.objects["Action.owner"] + target = bpy.context.scene.objects["Action.target"] + + action = bpy.data.actions.new("Slotted") + slot = action.slots.new('OBJECT', "Slot") + layer = action.layers.new(name="Layer") + strip = layer.strips.new(type='KEYFRAME') + strip.key_insert(slot, "location", 0, 2.0, 0.0) + strip.key_insert(slot, "location", 0, 7.0, 10.0) + + con = owner.constraints["Action"] + con.action = action + con.action_slot = slot + con.transform_channel = 'LOCATION_X' + con.min = -1.0 + con.max = 1.0 + con.frame_start = 0 + con.frame_end = 10 + + # Set the constrained object's location to something other than [0,0,0], + # so we can verify that it's actually replaced/mixed as appropriate to + # the mix mode. + owner.location = (2.0, 3.0, 4.0) + + con.mix_mode = 'REPLACE' + self.matrix_test("Action.owner", Matrix(( + (1.0, 0.0, 0.0, 4.5), + (0.0, 1.0, 0.0, 0.0), + (0.0, 0.0, 1.0, 0.0), + (0.0, 0.0, 0.0, 1.0) + ))) + + con.mix_mode = 'BEFORE_SPLIT' + self.matrix_test("Action.owner", Matrix(( + (1.0, 0.0, 0.0, 6.5), + (0.0, 1.0, 0.0, 3.0), + (0.0, 0.0, 1.0, 4.0), + (0.0, 0.0, 0.0, 1.0) + ))) + def main(): global args