Move a Character in MotionBuilder to the World’s Center with Python

‎With a dozen takes in my Motionbuilder scene and my Character placed at different locations on each take, I decided Python would help me move my Character to the World’s Center.

With raw Mocap data your Character’s Wold Position is all based on the Actors position within the volume during the capture (obvious). When working with a single Character I prefer to have  the Character positioned at the World’s Center.

Placing the Character at the World’s Center not only makes the math easier when supporting coverage of angles or distance, but it also keeps the scene organized and clean. Another nice thing is having the animations start frame reside on frame zero.

With Python being my new best friend within MotionBuilder I thought “I can script this!”.

I will go through my script for moving our test Character to the World Center below:

from pyfbsdk import *

##This Will Have To Be Changed To Reflect Your Charcter's Hip Name
##For This Test I Am Using Mia And Her Hips Are "Mia_Ctrl:HipsEffector"
lHipsEffector = FBFindModelByLabelName("Mia_Ctrl:HipsEffector")

##Get The Amount Of Frames That Are Within The Active Time Line
'''
We Will Need This As We Will Be Shifting Our Data Along The Time Line And We Want To Respect The Original Length Of The Time Line.
'''    
def GetFrameCount():
    ##Goto Start Frame And Store Frame Number
    FBPlayerControl().GotoStart()
    GetFrameCount.StartFrame = FBSystem().LocalTime.GetFrame()
    ##Goto End Frame And Store Frame Number
    FBPlayerControl().GotoEnd()
    GetFrameCount.EndFrame = FBSystem().LocalTime.GetFrame()
    ##Subtract Start Time From End Time To Get Our Time Span
    GetFrameCount.TimeSpan = GetFrameCount.EndFrame - GetFrameCount.StartFrame

##Create A Story Clip And Translate The Data To Start At The World Center     
def CreateStoryClip():
    ##Create A Track For Our Character:
    lTrack = FBStoryTrack(FBStoryTrackType.kFBStoryTrackCharacter, FBStory().RootFolder)
    ##Assign Our Current Character To The Track
    lTrack.Details.append(FBApplication().CurrentCharacter)
    ##Name Track
    lTrack.Label = "Del_Me-PythonStoryClip"
    ##Insert Current Take In The Newly Created Track
    CreateStoryClip.lClip = lTrack.CopyTakeIntoTrack( FBSystem().CurrentTake.LocalTimeSpan, FBSystem().CurrentTake )
    
    ##Move On Time Line To Prevent A Mobu Up Date Bug - This Has To Be Done To Ensure We Get The Correct Coordinates For The Hips
    FBPlayerControl().GotoNextKey()
    FBSystem().Scene.Evaluate()
    FBPlayerControl().GotoStart()
    FBSystem().Scene.Evaluate()
    
    ##Set Variable lCiplOffSet Off Of The Current Character's Hips Translational Values
    lClipOffSet = lHipsEffector.Translation
    ##Set Variables lClipOffSetX, lClipOffSetY, lClipOffSetZ
    lClipOffSetX, lClipOffSetY, lClipOffSetZ = lClipOffSet
    ##Set Variables lClipOffSetX, lClipOffSetY, lClipOffSetZ To Be A Negative Concatenate String
    lClipOffSetX, lClipOffSetY, lClipOffSetZ = -lClipOffSetX, -lClipOffSetY, -lClipOffSetZ
    ##Offset Clip To Start At The World Center
    CreateStoryClip.lClip.Translation = FBVector3d(lClipOffSetX, 0, lClipOffSetZ)

def ShiftStoryClip():
    ##Move Clip To Start At Frame Zero
    lMyStartTime = 0
    CreateStoryClip.lClip.Start = FBTime(0,0,0,lMyStartTime)

##Set The Time Line To Start At Zero And Be As Long As The GetFrameCount.TimeSpan Value
def StartTimeLineAtZero():
    ##Set Our Timeline To Be ==  GetFrameCount.TimeSpan In Length
    ##FBTime (0, 0, 0, 0, 0) = (Hours, Minutes, Seconds, Frames, Fields)
    FBSystem().CurrentTake.LocalTimeSpan = FBTimeSpan(FBTime(0, 0, 0, 0, 0), FBTime(0, 0, 0, GetFrameCount.TimeSpan, 0))

def PlotStoryClip():
    ##Deal With The User's Sory Mode Activity
    FBStory().Mute = False
    
    ##Plot Options
    lPlotClipOptions = FBPlotOptions()
    lPlotClipOptions.ConstantKeyReducerKeepOneKey = False
    lPlotClipOptions.PlotAllTakes = False
    lPlotClipOptions.PlotOnFrame = True
    lPlotClipOptions.PlotPeriod = FBTime( 0, 0, 0, 1 )
    lPlotClipOptions.PlotTranslationOnRootOnly = False
    lPlotClipOptions.PreciseTimeDiscontinuities = False
    lPlotClipOptions.RotationFilterToApply = FBRotationFilter.kFBRotationFilterUnroll
    lPlotClipOptions.UseConstantKeyReducer = False
    ##Plot Story Clip On Current Character
    lChar = FBApplication().CurrentCharacter
    lChar.PlotAnimation(FBCharacterPlotWhere.kFBCharacterPlotOnControlRig,lPlotClipOptions )
    
    ##Now To Delete Our Story Clip By Searching For "Del_Me-PythonStoryClip"
    for eachTrack in FBStory().RootFolder.Tracks:
        if eachTrack.Name == "Del_Me-PythonStoryClip":
            eachTrack.FBDelete()
        else:
            pass

##Run Functions In The Correct Order
def ProcessTake(): 
    GetFrameCount()
    CreateStoryClip()
    ShiftStoryClip()
    StartTimeLineAtZero()
    PlotStoryClip()

##Process Our Scene
ProcessTake()

This was fun! I found I was able to apply a lot of things I had learned over my short time exploring MotionBuilder’s Python Editor. The process (like all challenges) became problematic and solutions took time to figure out, but the sense of accomplishment is always amazing.

I hope this helps.

Add a Comment

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