Share

Your First Python Program

You can refer to this topic to gain a basic understanding of how to use Python in MotionBuilder. Additionally, some important classes and concepts are explained in this topic for understanding how to use Python.

About the Python Program

The Dancing Man program described in this topic constrains a character's hip effector to the screen coordinates (x,y) of the mouse. At the end of this step-by-step tutorial, a character appears to dance in the scene when you move the mouse. The image below shows the result of The Dancing Man program.

Opening the Python Editor

From the application menu, select Window Python Editor. The Python Editor appears. You can create and run Python programs in this editor.

Importing the MotionBuilder Python Module

The MotionBuilder Python API is contained in the pyfbsdk module. This module is generated from the MotionBuilder C++ API using Boost Python. The pyfbsdk module contains most of the Python classes included in the MotionBuilder SDK and many functions including the matrix and vector manipulation functions (FBMatrixInverse(), FBDot(), FBLength(), and others). For more information about pyfbsdk, refer to the Python API Reference section.

The first line of your MotionBuilder Python program must import the pyfbsdk module. You must also import the os module to interface with the operating system. The following code sample shows how to import the modules.

from pyfbsdk import *
import os

Clearing the Scene

First obtain a reference to MotionBuilder's instance of FBApplication. The FBApplication class provides most of the functionality available in the File application menu. As shown in following code sample, invoke FBApplication.FileNew(), which is equivalent to File New, but does not prompt to save before clearing the scene. For more information, see FBApplication - File I/O and Application Utilities.

# Get an instance of FBApplication and clear the scene with FileNew().
app = FBApplication()
app.FileNew()

Now, obtain a reference to MotionBuilder's scene, which is used to add a mouse device later. The FBSystem class is used to access the scene (FBScene), as well as track system properties such as the memory usage, application path, and MotionBuilder's version information. For more information, see FBSystem - The System Class.

# Get a reference to the underlying system properties of MotionBuilder.
system = FBSystem()

# Get a reference to the current MotionBuilder scene.
scene = system.Scene

Opening a File

The character file to open is in the MotionBuilder installation directory under OpenRealitySDK\scenes\PlasticMan.fbx. You need to define the GetMotionBuilderInstallationDirectory() helper function, which makes use of the FBSystem.ApplicationPath property to obtain the installation directory.

# Get the MotionBuilder installation directory.
#
# Note: FBSystem().ApplicationPath returns a string similar to:
#       'C:\Program Files\Autodesk\MotionBuilder <year>\bin\x64'.
#       We only want to return the substring:
#       'C:\Program Files\Autodesk\MotionBuilder <year>\
#
def GetMotionBuilderInstallationDirectory() :
    applicationPath = FBSystem().ApplicationPath
    return applicationPath[0:applicationPath.index('bin')]

Invoking FBApplication.FileOpen() with the second argument set to False opens the PlasticMan.fbx file without prompting through a dialog. For more information on opening, merging, and appending the contents of a file to the scene, see Loading from a File. Upon completion of FBapplication.FileOpen(), the PlasticMan character appears.

# Use the sample PlasticMan.fbx file in the default installation directory.
filename = GetMotionBuilderInstallationDirectory() + r'OpenRealitySDK\scenes\PlasticMan.fbx'
app.FileOpen(filename, False)
Note: You can save or load specific scene elements from a file after discarding other elements. For more information, see File Options.

Getting the Character's Hips Effector

You must manipulate the hips effector to make the PlasticMan character dance. Most objects in the scene are uniquely identified using their own namespace and name (see Names and Namespaces). You can also refer to Scene Elements for information about how to set up scenes, manipulate objects, and work with characters.

To obtain the hips effector, you can search forFBModel whose name corresponds to Plasticman_Ctrl:HipsEffector using FBFindModelByLabelName().

# Get a reference to the Plastic Man's hips effector.
hipEffector = FBFindModelByLabelName("Plasticman_Ctrl:HipsEffector")

The hipEffector variable now points to the FBModelMarker corresponding to the PlasticMan's hips effector. The FBModel base class is used to define the Translation, Rotation, and Scaling (TRS) of scene elements. The goal here is to constrain the translation and rotation of the PlasticMan's hips effector to the mouse input.

Adding a Mouse Device to the Scene

To use the mouse as an input device, you need to add a mouse (FBDevice) to the scene. You can use the FBCreateObject() function to obtain a mouse device. After the mouse object is created, you must activate it (FBDevice.OnLine), allow the application to read its data (FBBox.Live) , and append it to the scene's list of devices (FBScene.Devices).

# Create a mouse device and append it to the scene.
mouseDevice = FBCreateObject("Browsing/Templates/Devices", "Mouse", "MyMouse")
mouseDevice.OnLine = True
mouseDevice.Live = True
scene.Devices.append(mouseDevice)

The FBCreateObject() function is used to create objects from within the Asset Browser window. Following are the arguments of FBCreateObject():

  • Argument 1: "Browsing/Templates/Devices" - The Asset Browser path. This path is illustrated by the node hierarchy in the navigation tree. This path must always begin with Browsing/.

  • Argument 2: "Mouse" - The entry name. It corresponds to the red circle in the following figure.

  • Argument 3: "MyMouse" - The custom name of the object.

    Note: If you are attempting to create primitives in "Browsing/Templates/Elements/Primitives", the custom name must be the same as the entry name. For more information, refer to the BasicOperations/ObjectCreation.py example in the Python API Reference section.

Constraining the Character's Hips Effector to the Mouse

You can now constrain the character's hips effector to the mouse input. To do this, create a new FBConstraintRelation, and name it "DanceConstraint".

# Create a constraint relation.
constraintRelation = FBConstraintRelation("DanceConstraint")
constraintRelation.Active = True

The following figure illustrates how the hips effector is constrained by the mouse. Instances of FBBox and FBAnimationNode are used in an FBConstraintRelation to define the behavior of the constraint.

Animatable Elements - The FBBox Base Class

FBBox is the base class of any animatable element in the scene including models (FBModel), lights (FBLight), materials (FBMaterial), textures (FBTexture), shaders (FBShader), constraints (FBConstraint), and so on. Our FBConstraintRelation contains a set of FBBox objects. In the figure, the boxes MyMouse, Number to Vector, Number to Vector 1, and Plasticman_Ctrl:HipsEffector are all instances of FBBox. Each instance of FBBox performs a specific role in the FBConstraintRelation.

  • The MyMouse box acts as a data source.
  • The Plasticman_Ctrl:HipsEffector box is being constrained.
  • The Number to Vector and Number to Vector 1 boxes convert the X and Y data from the mouse into 3D vectors.

The following code sample shows how to create these boxes and set their position in the Constraint Settings window.

# 1. Create a box for the mouse's output.
mouseBox = constraintRelation.SetAsSource(mouseDevice)
constraintRelation.SetBoxPosition(mouseBox, 30, 30)

# 2. Create a box for the hip effector's input.
hipEffectorBox = constraintRelation.ConstrainObject(hipEffector)
constraintRelation.SetBoxPosition(hipEffectorBox, 700, 30)

# 3. Create a number to vector box to map the 'Y' output of the mouse
# to the 'Y' translation of the PlasticMan's hip effector.
translationConversionBox = constraintRelation.CreateFunctionBox('Converters', 'Number to Vector')
constraintRelation.SetBoxPosition(translationConversionBox, 300, 100)

# 4. Create a number to vector box to map the 'X' output of the mouse
# to the 'X' and 'Z' rotation of the PlasticMan's hip effector.
rotationConversionBox = constraintRelation.CreateFunctionBox('Converters', 'Number to Vector')
constraintRelation.SetBoxPosition(rotationConversionBox, 300, 0)

Animatable Properties - The FBAnimationNode Class

Instances of FBAnimationNode are contained within an FBBox. In the figure, the animation nodes are denoted by arrows. An FBAnimationNode exists for any animatable property of an FBBox. For example, the Plasticman_Ctrl:HipsEffector box provides one input animation node for each of its animatable properties: translation, rotation, and scaling.

  • Arrows on the left side of a box correspond to input animation nodes.
  • Arrows on the right side of a box correspond to output animation nodes.

The utility function FindAnimationNode() defined at the beginning of the program returns the child animation node whose name corresponds to the one specified by pName.

# Define a utility function to look up a named
# animation node under a given parent node.
def FindAnimationNode( pParent, pName ):
    lResult = None
    for lNode in pParent.Nodes:
        if lNode.Name == pName:
            lResult = lNode
            break
    return lResult

You can extract the animation nodes of interest and connect them together using FBConnect() as shown in the following code sample.

# Get a reference to all the animation nodes we need to connect.
# Mouse output:
xMouseOut = FindAnimationNode(mouseBox.AnimationNodeOutGet(), 'X')
yMouseOut = FindAnimationNode(mouseBox.AnimationNodeOutGet(), 'Y')

# Translation conversion:
yTranslationConversionIn = FindAnimationNode(translationConversionBox.AnimationNodeInGet(), 'Y')
translationConversionOut = FindAnimationNode(translationConversionBox.AnimationNodeOutGet(), 'Result')

# Rotation conversion:
xRotationConversionIn = FindAnimationNode(rotationConversionBox.AnimationNodeInGet(), 'X')
zRotationConversionIn = FindAnimationNode(rotationConversionBox.AnimationNodeInGet(), 'Z')
rotationConversionOut = FindAnimationNode(rotationConversionBox.AnimationNodeOutGet(), 'Result')

# Hip effector input:
hipTranslationIn = FindAnimationNode(hipEffectorBox.AnimationNodeInGet(), 'Translation')
hipRotationIn = FindAnimationNode(hipEffectorBox.AnimationNodeInGet(), 'Rotation')

# Connect the animation nodes in the constraint.
FBConnect(yMouseOut, yTranslationConversionIn)
FBConnect(xMouseOut, xRotationConversionIn)
FBConnect(xMouseOut, zRotationConversionIn)
FBConnect(translationConversionOut, hipTranslationIn)
FBConnect(rotationConversionOut, hipRotationIn)

Saving to a File with a Dialog

Create a dialog box using FBFilePopup(), and configure it for saving.

# Save the file using a dialog box.
saveDialog = FBFilePopup()
saveDialog.Style = FBFilePopupStyle.kFBFilePopupSave
saveDialog.Filter = '*.fbx'

saveDialog.Caption = 'My Save Dialog Box'
# Set the path to the current user's My Documents\MB folder.
saveDialog.Path = os.path.expanduser('~') + r'\Documents\MB'
saveDialog.FileName = 'DancingMan.fbx'

if saveDialog.Execute():
    app.FileSave(saveDialog.FullFilename)
Note: A current issue in the MotionBuilder SDK requires that the FBFilePopup.Filter property be set to "*". Failure to do so might result in a crash.

You can also refer to Saving to a File and User Interface for more information.

The Dancing Man Program

The following code sample is the complete Dancing Man program that you can copy into the Python Editor and run. Change the filename value to a suitable path if you are not using the default 64-bit installation of MotionBuilder. You also need to change the Plasticman_Ctrl:HipsEffector string if your file contains a different character.

from pyfbsdk import *
import os

# Define a utility function to look up a named
# animation node under a given parent node.
def FindAnimationNode( pParent, pName ):
    lResult = None
    for lNode in pParent.Nodes:
        if lNode.Name == pName:
            lResult = lNode
            break
    return lResult

# Get the MotionBuilder installation directory.
#
# Note: FBSystem().ApplicationPath returns a string similar to:
#       'C:\Program Files\Autodesk\MotionBuilder <year>\bin\x64'.
#       We only want to return the path:
#       'C:\Program Files\Autodesk\MotionBuilder <year>\
def GetMotionBuilderInstallationDirectory() :
    applicationPath = FBSystem().ApplicationPath
    return applicationPath[0:applicationPath.index('bin')]



# Get the instance of FBApplication, and clear the scene with FileNew().
app = FBApplication()
app.FileNew()



# Get a reference to the underlying system properties of MotionBuilder.
system = FBSystem()



# Get a reference to the current MotionBuilder scene.
scene = system.Scene



# Use the sample PlasticMan.fbx file in the default installation directory.
filename = GetMotionBuilderInstallationDirectory() + r'OpenRealitySDK\scenes\PlasticMan.fbx'
app.FileOpen(filename, False)



# Get a reference to the Plastic Man's hips effector.
hipEffector = FBFindModelByLabelName('Plasticman_Ctrl:HipsEffector')



# Create a mouse device and append it to the scene.
mouseDevice = FBCreateObject('Browsing/Templates/Devices', 'Mouse', 'MyMouse')
mouseDevice.Online = True
mouseDevice.Live = True
scene.Devices.append(mouseDevice)



# Create a constraint relation.
constraintRelation = FBConstraintRelation('DanceConstraint')
constraintRelation.Active = True



# 1. Create a box for the mouse's output.
mouseBox = constraintRelation.SetAsSource(mouseDevice)
constraintRelation.SetBoxPosition(mouseBox, 30, 30)

# 2. Create a box for the hip effector's input.
hipEffectorBox = constraintRelation.ConstrainObject(hipEffector)
constraintRelation.SetBoxPosition(hipEffectorBox, 700, 30)

# 3. Create a number to vector box to map the 'Y' output of the mouse
# to the 'Y' translation of the PlasticMan's hip effector.
translationConversionBox = constraintRelation.CreateFunctionBox('Converters', 'Number to Vector')
constraintRelation.SetBoxPosition(translationConversionBox, 300, 100)

# 4. Create a number to vector box to map the 'X' output of the mouse
# to the 'X' and 'Z' rotation of the PlasticMan's hip effector.
rotationConversionBox = constraintRelation.CreateFunctionBox('Converters', 'Number to Vector')
constraintRelation.SetBoxPosition(rotationConversionBox, 300, 0)



# Get a reference to all the animation nodes we need to connect.
# Mouse output:
xMouseOut = FindAnimationNode(mouseBox.AnimationNodeOutGet(), 'X')
yMouseOut = FindAnimationNode(mouseBox.AnimationNodeOutGet(), 'Y')

# Translation conversion:
yTranslationConversionIn = FindAnimationNode(translationConversionBox.AnimationNodeInGet(), 'Y')
translationConversionOut = FindAnimationNode(translationConversionBox.AnimationNodeOutGet(), 'Result')

# Rotation conversion:
xRotationConversionIn = FindAnimationNode(rotationConversionBox.AnimationNodeInGet(), 'X')
zRotationConversionIn = FindAnimationNode(rotationConversionBox.AnimationNodeInGet(), 'Z')
rotationConversionOut = FindAnimationNode(rotationConversionBox.AnimationNodeOutGet(), 'Result')

# Hip effector input:
hipTranslationIn = FindAnimationNode(hipEffectorBox.AnimationNodeInGet(), 'Translation')
hipRotationIn = FindAnimationNode(hipEffectorBox.AnimationNodeInGet(), 'Rotation')


# Connect the animation nodes in the constraint.
FBConnect(yMouseOut, yTranslationConversionIn)
FBConnect(xMouseOut, xRotationConversionIn)
FBConnect(xMouseOut, zRotationConversionIn)
FBConnect(translationConversionOut, hipTranslationIn)
FBConnect(rotationConversionOut, hipRotationIn)



# Save the file using a dialog box.
saveDialog = FBFilePopup()
saveDialog.Style = FBFilePopupStyle.kFBFilePopupSave
saveDialog.Filter = '*.fbx'

saveDialog.Caption = 'My Save Dialog Box'
# Set the path to the current user's My Documents\MB folder.
saveDialog.Path = os.path.expanduser('~') + r'\Documents\MB'
saveDialog.FileName = 'DancingMan.fbx'

if saveDialog.Execute():
    app.FileSave(saveDialog.FullFilename)

Was this information helpful?