FBPose - Poses
 
 
 

What is a Pose?

The FBPose class encapsulates MotionBuilder's ability to take a snapshot of characters (FBCharacterPose), and objects (FBObjectPose).

Creating a Character Pose

A character pose is created by invoking the FBCharacterPose() constructor. This adds an empty pose item in the scene's list of poses, as well as in MotionBuilder's Pose Controls UI element.

Given a reference to an FBCharacter in the scene (for example: FBApplication.CurrentCharacter), the FBCharacterPose.CopyPose() function stores the given character's pose into that FBCharacterPose object. Pose creation is illustrated in the code snippet below.

# Create poses of the character.
def createPoses(pCharacter):   
    poseList = []
    
    # Create our first pose.
    pose1 = FBCharacterPose('myPose1')
    pose1.CopyPose(pCharacter)
    poseList.append(pose1)
    
    # Obtain a reference to the character's effectors.
    hipsEffector = pCharacter.GetEffectorModel(FBEffectorId.kFBHipsEffectorId)
    
    # Create our second pose.
    hipsEffector.Translation = FBVector3d(0, 50, 0)
    # (!!!) Note: It is very important to invoke FBSystem().Scene.Evaluate() 
    #       at this step to ensure the hipsEffector has effectively been translated. 
    #       Otherwise, it is not guaranteed that MotionBuilder's evaluation thread 
    #       will have translated the hips effector before pose2.CopyPose() is called.
    FBSystem().Scene.Evaluate()
    pose2 = FBCharacterPose('myPose2')
    pose2.CopyPose(pCharacter)
    poseList.append(pose2)
    
    # Repeat pose1 in our poseList to enable looping later.
    poseList.append(pose1)
    
    return (poseList)

Applying a Pose to a Character

The FBCharacterPose.PastePose() function applies the data contained in FBCharacterPose to the specified character. An instance of FBCharacterPoseOptions can also be specified to ensure the pose is mirrored, or to enable full-body keying mode. The code snippet below illustrates how to apply a list of poses to a character in the scene, and how to use these poses as animation key frames.

# Create keys from the pose list.
def createKeys(pCharacter, pPoseList):
    # Ensure we take a pose of the entire character's body.
    poseOptions = FBCharacterPoseOptions()
    poseOptions.mCharacterPoseKeyingMode = FBCharacterPoseKeyingMode.kFBCharacterPoseKeyingModeFullBody
    poseOptions.SetFlag(FBCharacterPoseFlag.kFBCharacterPoseMirror, True)
    
    # Select the character's control rig (1st and 3rd parameter = True). This 
    # allows FBPlayerControl.Key() to key the selected models.
    pCharacter.SelectModels(True, False, True, False)
    
    # Create a pose every 15 frames using our pose list.
    lPlayer = FBPlayerControl()
    poseCount = 0
    frameInterval = 15
    for pose in pPoseList:
        # Go to the next frame.
        lPlayer.Goto(FBTime(0, 0, 0, frameInterval * poseCount))
        FBSystem().Scene.Evaluate()
        # Apply the pose to the character using our pose options.
        pose.PastePose(pCharacter, poseOptions)
        FBSystem().Scene.Evaluate()
        # Create a key in the player.
        lPlayer.Key()
        FBSystem().Scene.Evaluate()
        # Increment our counter.
        poseCount += 1
    
    # De-select the character's control rig (1st parameter = False, 3rd parameter = True).
    pCharacter.SelectModels(False, False, True, False)
    
    # Return the frame number of the last frame keyed.
    return frameInterval * (poseCount - 1)

Example: Animating a Character With Poses.

Sample Viewport Output:

Program Summary: The following program builds on the program presented in FBCharacter - Characters. We define two new functions to make the character perform a looping "crouching" animation:

from pyfbsdk import *

# In the following skeleton template, the "LeftUpLeg" 
# has "Hips" as its parent, and its local (x,y,z) translation is (15, -10, 0).
jointMap = {
    #jointName,     (parentName,       translation  )
    'Reference':    (None,           (  0,   0,  0)),
    'Hips':         ('Reference',    (  0,  75,  0)),    
    'LeftUpLeg':    ('Hips',         ( 15, -10,  0)),
    'LeftLeg':      ('LeftUpLeg',    (  0, -30,  0)),
    'LeftFoot':     ('LeftLeg',      (  0, -30,  0)),     
    'RightUpLeg':   ('Hips',         (-15, -10,  0)),
    'RightLeg':     ('RightUpLeg',   (  0, -30,  0)),
    'RightFoot':    ('RightLeg',     (  0, -30,  0)),     
    'Spine':        ('Hips',         (  0,  40,  0)),
    'LeftArm':      ('Spine',        ( 20,  10,  0)),
    'LeftForeArm':  ('LeftArm',      ( 30,   0,  0)),
    'LeftHand':     ('LeftForeArm',  ( 30,   0,  0)),    
    'RightArm':     ('Spine',        (-20,  10,  0)),
    'RightForeArm': ('RightArm',     (-30,   0,  0)),
    'RightHand':    ('RightForeArm', (-30,   0,  0)),
    'Head':         ('Spine',        (  0,  30,  0)),
}

###############################################################
# Helper Function(s).                                         #
###############################################################

# Create a skeleton in a T-pose facing along the positive Z axis
def createSkeleton(pNamespace):
    global jointMap
    skeleton = {}
    
    # Populate the skeleton with joints.
    for jointName, (parentName, translation) in jointMap.iteritems():
        if jointName == 'Reference' or jointName == 'Hips':
            # If it is the reference node, create an FBModelRoot.
            joint = FBModelRoot(jointName)
            
        else:
            # Otherwise, create an FBModelSkeleton.
            joint = FBModelSkeleton(jointName)
        
        joint.LongName = pNamespace + ':' + joint.Name # Apply the specified namespace to each joint.
        joint.Color = FBColor(0.3, 0.8, 1)             # Cyan
        joint.Size = 250                               # Arbitrary size: big enough to see in viewport 
        joint.Show = True                              # Make the joint visible in the scene.
        
        # Add the joint to our skeleton.
        skeleton[jointName] = joint
    
    # Once all the joints have been created, apply the parent/child 
    # relationships to each of the skeleton's joints.
    for jointName, (parentName, translation) in jointMap.iteritems():
        # Only assign a parent if it exists.
        if parentName != None and parentName in jointMap.keys():
            skeleton[jointName].Parent = skeleton[parentName] 
        
        # The translation should be set after the parent has been assigned.
        skeleton[jointName].Translation = FBVector3d(translation)

    ''' The following is a much longer way to write the above function:
    
    reference = FBModelRoot('Reference')
    
    hips = FBModelRoot('Hips')
    hips.Color = FBColor(0.3, 0.8, 1)
    hips.Size = 250
    hips.Translation = FBVector3d(0, 75, 0)
    hips.Parent = reference
    skeleton['Hips'] = hips
    
    spine = createSkeletonNode('Spine')
    spine.Parent = hips
    spine.Color = FBColor(0.3, 0.8, 1)
    spine.Size = 250
    spine.Translation = FBVector3d(0, 25, 0)
    skeleton['Spine'] = spine
    
    # ...
    '''
    return skeleton
    
# Create a model which will be applied to each joint in the skeleton.
def createModel():
    # Create a sphere.
    model = FBCreateObject('Browsing/Templates/Elements/Primitives', 'Sphere', 'Sphere')
    model.Scaling = FBVector3d(0.9, 0.9, 0.9)
    
    # Define a slightly reflective dark material.
    material = FBMaterial('MyMaterial')
    material.Ambient = FBColor(0, 0, 0)
    material.Diffuse = FBColor(0, 0.04, 0.08)
    material.Specular = FBColor(0, 0.7, 0.86)
    material.Shininess = 100
    model.Materials.append(material)

    # Create a cartoon-like shader.
    shader = FBCreateObject('Browsing/Templates/Shading Elements/Shaders', 'Edge Cartoon', 'MyShader')
    
    # For a list of all the shader's properties do:
    #for item in shader.PropertyList:
    #    print item.Name
    aliasProp = shader.PropertyList.Find('Antialiasing')
    aliasProp.Data = True
    colorProp = shader.PropertyList.Find('EdgeColor')
    colorProp.Data = FBColor(0, 0.83, 1)
    widthProp = shader.PropertyList.Find('EdgeWidth')
    widthProp.Data = 8
    
    # Append the cartoon shader to the model.
    model.Shaders.append(shader)        
    
    # The default shader must also be applied to the model.
    defaultShader = FBSystem().Scene.Shaders[0]
    model.Shaders.append(defaultShader) 
    
    # Use the default shading mode.
    model.ShadingMode = FBModelShadingMode.kFBModelShadingDefault
    
    return model

# Apply a copy of pModel to each joint in the skeleton. 
def applyModelToSkeleton(pSkeleton, pModel):   
    # Create a copy of the model for each joint in the skeleton.
    for jointName, joint in pSkeleton.iteritems():
        if jointName == 'Reference':
            # Do not apply the model to the Reference node.
            continue
        
        # Parent the copied model to the joint.
        model = pModel.Clone()
        model.Parent = joint        
        model.Show = True
        
        # Use the joint name as a prefix.
        model.Name = jointName + pModel.Name

        # Reset the model's translation to place it at the same 
        # location as its parent joint.
        model.Translation = FBVector3d(0, 0, 0)

# Characterize the skeleton and create a control rig.
def characterizeSkeleton(pCharacterName, pSkeleton):
    # Create a new character.
    character = FBCharacter(pCharacterName)
    FBApplication().CurrentCharacter = character
    
    # Add each joint in our skeleton to the character.
    for jointName, joint in pSkeleton.iteritems():
        slot = character.PropertyList.Find(jointName + 'Link')
        slot.append(joint)

    # Flag that the character has been characterized.
    character.SetCharacterizeOn(True)
    
    # Create a control rig using Forward and Inverse Kinematics,
    # as specified by the "True" parameter.
    character.CreateControlRig(True)
    
    # Set the control rig to active.
    character.ActiveInput = True
    
    return character

# Create poses of the character.
def createPoses(pCharacter):   
    poseList = []
    
    # Create our first pose.
    pose1 = FBCharacterPose('myPose1')
    pose1.CopyPose(pCharacter)
    poseList.append(pose1)
    
    # Obtain a reference to the character's effectors.
    hipsEffector = pCharacter.GetEffectorModel(FBEffectorId.kFBHipsEffectorId)
    
    # Create our second pose.
    hipsEffector.Translation = FBVector3d(0, 50, 0)
    # (!!!) Note: It is very important to invoke FBSystem().Scene.Evaluate() 
    #       at this step to ensure the hipsEffector has effectively been translated. 
    #       Otherwise, it is not guaranteed that MotionBuilder's evaluation thread 
    #       will have translated the hips effector before pose2.CopyPose() is called.
    FBSystem().Scene.Evaluate()
    pose2 = FBCharacterPose('myPose2')
    pose2.CopyPose(pCharacter)
    poseList.append(pose2)
    
    # Repeat pose1 in our poseList to enable looping later.
    poseList.append(pose1)
    
    return (poseList)

# Create keys from the pose list.
def createKeys(pCharacter, pPoseList):
    # Ensure we take a pose of the entire character's body.
    poseOptions = FBCharacterPoseOptions()
    poseOptions.mCharacterPoseKeyingMode = FBCharacterPoseKeyingMode.kFBCharacterPoseKeyingModeFullBody
    poseOptions.SetFlag(FBCharacterPoseFlag.kFBCharacterPoseMirror, True)
    
    # Select the character's control rig (3rd parameter = True). This 
    # allows FBPlayerControl.Key() to key the selected models.
    pCharacter.SelectModels(True, False, True, False)
    
    # Create a pose every 15 frames using our pose list.
    lPlayer = FBPlayerControl()
    poseCount = 0
    frameInterval = 15
    for pose in pPoseList:
        # Go to the next frame.
        lPlayer.Goto(FBTime(0, 0, 0, frameInterval * poseCount))
        FBSystem().Scene.Evaluate()
        # Apply the pose to the character using our pose options.
        pose.PastePose(pCharacter, poseOptions)
        FBSystem().Scene.Evaluate()
        # Create a key in the player.
        lPlayer.Key()
        FBSystem().Scene.Evaluate()
        # Increment our counter.
        poseCount += 1
    
    # De-select the character's control rig (1st parameter = False).
    pCharacter.SelectModels(False, False, True, False)
    
    # Return the frame number of the last frame keyed.
    return frameInterval * (poseCount - 1)
    
    
###############################################################
# Main.                                                       #
###############################################################

# Clear the scene.
FBApplication().FileNew()

# Create a new skeleton.
characterName = 'MyStickman'
skeleton = createSkeleton(characterName)

# Apply a model to each limb of the skeleton.
templateModel = createModel()
applyModelToSkeleton(skeleton, templateModel)
templateModel.FBDelete() # We do not need the template model anymore.

# Characterize the skeleton and create a control rig.
character = characterizeSkeleton(characterName, skeleton)

# Create the poses
poseList = createPoses(character)

# Create keys in the player control.
lastFrame = createKeys(character, poseList)

# Play the animation.
lPlayer = FBPlayerControl()
lPlayer.LoopStart = FBTime(0, 0, 0, 0)         # Set the start time in the player control.
lPlayer.LoopStop = FBTime(0, 0, 0, lastFrame)  # Set the end time in the player control.
lPlayer.LoopActive = True                      # Enable playback looping.
lPlayer.GotoStart()                            # Play the animation from the beginning.
lPlayer.Play()