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.