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)
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 withBrowsing/
.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
andNumber 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)
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)