Home Website Youtube GitHub

Flip animation, not just pose

Hi, is there a premade way in mgear to flip an animation in a time range and not just a single pose? Or has someone made a script to do this? I guess its not enough to simply loop through all keyframes in a time range and flip them? Because the handles of the animation curves might not get flipped?

I do not know of a premade script, but yes in theory. You should be able to loop through the keys on a range and flip pose per frame/key.

It really depends on the setup of the rig and how well it translates to being flipped via a script.

for current_key in frame_range; flip(all_controls); set_key();

How I imagine it would go.

I see, thank you. I’ll give it a try! I think I also might have to find a way to get the state / length / rotation of the tangents of the keys to apply them after flipping. Otherwise the animation curves won’t be the same.

I believe Animbot can do this.

1 Like

I would rather not tie rig functionality to a subscription service :(. I’ll try to write a script and post it here.

1 Like

I wrote a first version of a script that will flip the animation of a mgear rig in the current playback range. I am not a programmer, so suggestions for improvements are very welcome :).

It does not flip the values of the extra attributes in the hostUI controllers and gives a warning. Not sure how I can get this to work.

Warning: Flip/Mirror pose fail #
Traceback (most recent call last):
File "C:\Users\Martin\Documents\maya\modules\scripts\mgear\core\anim_utils.py", line 976, in mirrorPose
mirrorEntries.extend(gatherMirrorData(nameSpace, oSel, flip))
File "C:\Users\Martin\Documents\maya\modules\scripts\mgear\core\anim_utils.py", line 1036, in gatherMirrorData
return calculateMirrorData(node, oTarget, flip=flip)
File "C:\Users\Martin\Documents\maya\modules\scripts\mgear\core\anim_utils.py", line 1085, in calculateMirrorData
flipVal = targetNode.attr(attrName).get()
File "C:\Program Files\Autodesk\Maya2018\Python\lib\site-packages\pymel\core\nodetypes.py", line 2009, in attr
raise e
MayaAttributeError: Maya Attribute does not exist (or is not unique):: u'legUI_L0_ctl.leg_R0_ctl'
Maya Attribute does not exist (or is not unique):: u'legUI_L0_ctl.leg_R0_ctl' 

Another limitation is that it only works without namespaces right now.

I also had to call the mgear mirrorPose() function for each controller separately in a for loop, because it does not flip the pose with a list as an input and gives the following error / warning when I call it :

Warning: Flip/Mirror pose fail #
Traceback (most recent call last):
File "C:\Users\Martin\Documents\maya\modules\scripts\mgear\core\anim_utils.py", line 972, in mirrorPose
nameSpace = getNamespace(nodes[0])
File "C:\Users\Martin\Documents\maya\modules\scripts\mgear\core\anim_utils.py", line 260, in getNamespace
if len(modelName.split(":")) >= 2:
AttributeError: 'list' object has no attribute 'split'
'list' object has no attribute 'split'

And I commented the function call of “bake_anim_range()” out, because the flip_animation function does not work, if called right after the bake function. It runs until the end, shows no errors, but does not flip the animation. So I have to call them in two steps :man_facepalming:. Again not sure what I can do about it :).

Here is the script:

import pymel.core as pm
from mgear.core.anim_utils import mirrorPose


class flip_animation():
    
    
    def __init__(self):
        self.rig_grp = 'rigGRP_controllers_grp'
        self.controllers = []
        self.flip_ctls = []
        self.range_start = int(pm.playbackOptions(q=True, min=True))
        self.range_end = int(pm.playbackOptions(q=True, max=True))


    def get_mgear_controllers(self):
        for obj in pm.PyNode(self.rig_grp).members():
            if pm.nodeType(obj) != 'objectSet':
                self.controllers.append(obj)
            if pm.nodeType(obj) == 'objectSet':
                for member in obj.members():
                    self.controllers.append(member)
        return self.controllers            
 
 
    def set_flip_controllers(self):
        all_ctls_list = [pm.PyNode(x) for x in self.get_mgear_controllers()]    
        for ctl in all_ctls_list:
            if not '_L0_' in str(ctl):  # exclude left side to prevent double flipping
                self.flip_ctls.append(ctl)  
        return self.flip_ctls
        
 
    def bake_anim_range(self):
        ctls_list = self.get_mgear_controllers()
        pm.bakeResults( 
                        ctls_list, 
                        time=(self.range_start,self.range_end), 
                        preserveOutsideKeys=True, 
                        simulation=True
                    )                   


    @staticmethod
    def flip_pose(ctls_list):
        for ctl in ctls_list:           
            mirrorPose(flip=True, nodes=[ctl])

    
    def set_key(self):
        ctls_list = self.get_mgear_controllers()
        pm.setKeyframe(ctls_list)
                
                
    def flip_animation(self):  
        ctls_list = self.set_flip_controllers()    
        for frame in range(self.range_start, self.range_end+1):
            pm.currentTime(frame)
            self.flip_pose(ctls_list)
            self.set_key()
                                            
                    
if __name__ == '__main__':
    
    flip = flip_animation()        
    # flip.bake_anim_range()
    flip.flip_animation()
2 Likes

Hello @raumkapsel
Thanks for sharing your script here. I will check it ASAP

1 Like

Thank you very Much @Miquel !

In the future I would really like to get rid of the baking step, before flipping the animation. But for now I couldn’t figure out how to flip keyframe tangent handles to preserve the animation curves without baking.