MotionBuilder Python – Mirror Character Animation Through Story Mode

One of my first posts on this blog showed how to mirror animations via python. The script would set the mirrored option to “ON” for the character and then plot the animation to the Skeleton and then back to the Control Rig – which works. Recently I was asked if there was a way to mirror an animation along a specific axis and there is via Story Mode. In this post I will go over how we can mirror animations via Story Mode using Python scripting in MotionBuilder.

Below you will find the script which uses a currently selected character and copies the data to a new take called “original_take_name-Mirrored”. The script will mirror the character’s data based on the users desired Plane Option (XY, ZY, XZ) by using FBStoryClipMirrorPlane.

import pyfbsdk as fb

##Create A New Take
def CreateMirrorTake():
    takeName = fb.FBSystem().CurrentTake.LongName+"_Mirrored"
    fb.FBSystem().CurrentTake.CopyTake( takeName )

##Plot The Story Clip
def PlotCharacterClip( character = object ):
    lPlotClipOptions = fb.FBPlotOptions()
    lPlotClipOptions.ConstantKeyReducerKeepOneKey = False
    lPlotClipOptions.PlotAllTakes = False
    lPlotClipOptions.PlotOnFrame = True
    lPlotClipOptions.PlotPeriod = fb.FBTime( 0, 0, 0, 1 )
    lPlotClipOptions.PlotTranslationOnRootOnly = False
    lPlotClipOptions.PreciseTimeDiscontinuities = False
    lPlotClipOptions.RotationFilterToApply = fb.FBRotationFilter.kFBRotationFilterUnroll
    lPlotClipOptions.UseConstantKeyReducer = False
    character.PlotAnimation (fb.FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton,lPlotClipOptions )               
    character.PlotAnimation(fb.FBCharacterPlotWhere.kFBCharacterPlotOnControlRig,lPlotClipOptions ) 

##Send Character To A Story Clip And Plot The Data Mirrored
def MirrorCharacter( character = object, XY = False, ZY = False, XZ = False ):
    if XY == ZY == XZ == False: pass
    
    else:
        ##Create A Character Stroy Track And Add Currect Take As A Clip
        track = fb.FBStoryTrack( fb.FBStoryTrackType.kFBStoryTrackCharacter, fb.FBStory().RootFolder )
        track.Details.append( character )
        track.CopyTakeIntoTrack( fb.FBSystem().CurrentTake.LocalTimeSpan, fb.FBSystem().CurrentTake )
        
        ##Turn Story Mode "ON"
        orgStoryMute = fb.FBStory().Mute
        if orgStoryMute == True:
            fb.FBStory().Mute = False
    
        ##Get Mirror Plane Options
        if XY == True: mirrorplaneOption = fb.FBStoryClipMirrorPlane.kFBStoryClipMirrorPlaneXY
        if ZY == True: mirrorplaneOption = fb.FBStoryClipMirrorPlane.kFBStoryClipMirrorPlaneZY
        if XZ == True: mirrorplaneOption = fb.FBStoryClipMirrorPlane.kFBStoryClipMirrorPlaneXZ
        ##Set All Clips On Provided Track To Be Mirrored
        for clip in track.Clips:
            clip.SolvingMode        = fb.FBStoryClipSolveMode.kFBStoryClipRetargetSkeleton  ##Sets The Solving Mode To "RetargetSkeleton" Which Seems To Give Mirrored Result
            clip.MirrorPlane        = mirrorplaneOption                                     ##Set Mirror Plane Options Based Off Of The User's Checked Box 
            clip.MirrorAnimation    = True
        
        ##Plot Mirrored Story Data Back To Take
        PlotCharacterClip( character )
        
        ##Delete Stroy Track
        track.FBDelete()
        
        ##Set Story Mute Back To Original Setting
        fb.FBStory().Mute = orgStoryMute
        

        CreateMirrorTake()

##Mirror Current Character Along Mirror Plane ZY    
MirrorCharacter( fb.FBApplication().CurrentCharacter, XY = False, ZY = True, XZ = False  )

I hope this helps.

2 Comments

Add a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.