eduardo simioni

Tag: motionbuilder

right click signal in PyQt/PySide

by on Jun.10, 2014, under Maya, MotionBuilder, pyqt, pyside, python

It’s extremely easy, if you know it’s called customContextMenuRequested:

As for example:

btnLeft.customContextMenuRequested.connect( lambda: self.RMB(btnLeft, oModel) )

It also has QPoint as default value returning the pixel where right click/contextMenu was requested:

btnLeft.customContextMenuRequested.connect( self.asd )

def asd(self, pos)
    print pos
Leave a Comment :, , , , , , more...

creating a dynamic menu system in Motionbuilder

by on Feb.16, 2014, under GUI, MotionBuilder, pipeline, python

There are two main tricky parts in doing this:

1) Organizing your tools
2) The event function

Prior to face this sort of task you need to be aware of the concepts explained on the previous post, python modules and import system and have a very very very good understanding of FBMenuManager.

Organizing your tools

In short, there are two ways of accomplishing this. One is having each menu entry as a simple .py script which is executed with execfile(), the other is to think of each tool as a module/folder. If you want to group stuff into different menus, it helps to think of each menu as a different empty module/folder as well.

So let’s suppose you want a menu structure inside Motionbuilder like this:

  • Scripts
    • Cinematics
      • Mocap Cleaner
      • Mocap Destroyer
    • Gameplay
      • Import Tool
      • Export Tool
    • Generic
      • Animate_Everything
      • Fix_Animation

Cinematics, Gameplay and Generic are the main menus, and they show on the right of “Help”. Mocap Cleaner, Mocap Destroyer, Import Tool, Export Tool are (imaginary) full featured tools, each one in its own folder with multiple files. Animate_Everything and Fix_Animation are simple .py scripts, and they are grouped in one single folder. Like this:

Files organization

The Generic scripts is quite simple to do and it’s covered down below. The way I did for full featured tools though is, obviously, creating one menu entry for each folder inside the Scripts folder. Each of these “menu folders” had a (see below in initialization) with some variables describing their content and would become a main entry besides the “Help” menu. Each folder would then be added to sys.path and create one of these data structures:

class MenuData():
    This stores all necessary information for one new menu to be constructed it`s sub menu items.
    def __init__(self, sMenuName, sFullPath, mMenuModule):
        # menu information
        self.sMenuName   = sMenuName
        self.mMenuModule = mMenuModule
        self.sFullPath   = sFullPath
        # actual FBGenericMenu object for the menu
        self.oMenu       = None
        # the key one, list of tools in tuples, 0 is name, 1 is module or full script path (for generics)
        self.lmTools     = []

So you end up with a list of MenuData’s, one for Cinematic, Gameplay and Generic. Now you need to create the actual FBGenericMenu for each folder. You could simplify this into one single for loop, if you have all tools into the Scripts root you could even do away with the MenuData (which is to create one menu per folder), but I wanted to do something generic and support many different outcomes so I was doing it in two loops described below.

Initializing Tools:

For each MenuData I look for each subfolder (Scripts/Gameplay/Import, Scripts/Gameplay/ExportTool for example) and save each module (present in this subfolder) into MenuData.lmTools. The tricky part is how to do this:

for oMenu in listOfMenuData:
    lDir = os.listdir( oMenu.sFullPath )
    for sPath in lDir:
        sMenuPathWithSubFolder = oMenu.sFullPath + "/" + sPath
        if os.path.isdir( sMenuPathWithSubFolder ) and os.path.isfile( sMenuPathWithSubFolder+"/" ):
            module = __import__( sPath )
            oMenu.lmTools.append( (sPath, module ) )

Line 6 works because each “Menu/Module” path was added to sys.path, so you can easily import each tool without its entire path. The important line though is the last one. It adds a list of tools to MenuData.lmTools with a tuple that contains (“string to the tool module”, “the tool module itself”). And here is why you need to add the menu folder to sys.path. You could still get the tool module with other means, but by having Gameplay in sys.path you can do import ImportTool on other tools and share code between them.

On the tool folder you need a so python can import that as a package. You can (or need) to put variables in there, so the menu system can create the proper menu entry for the tool (to use a custom name, for example). This is an example of what I’m using on /Scripts/Gameplay/Destroyer/

sToolFullName = "Mocap Destroyer"
sStartFileToImport = "destroyerDialog"
sCustomFunction = "mainUI()"

It is important that there’s no code running in this __init__. This file is loaded and ran during Motionbuilder startup, so if there’s something crashing in there the entire menu system won’t load (unless you don’t trust your colleagues and populate it with try/catches).

And here is where python magic happens. Once you loaded the module in MenuData you can access all the contents from that with for example:

print oMenu.lmTools[1].sToolFullName
print oMenu.lmTools[1].sTartFileToImport

And this feature you will need to use on the next two steps.

Create menu entries

You haven’t created anything yet because you need to know WHAT to create. That’s why I was importing each tool before actually creating FBGenericMenuItems. This way the title of the menu item can be set on the tool itself, instead of relying on a separate file altogether.

But when dealing with FBGenericMenu is best to just use code instead of describing it:

oMenuMngr = fb.FBMenuManager()
for oMenu in listOfMenuData:
    # FBGenericMenu
    oMenuMngr.InsertLast( None, oMenu.sMenuName )
    oFBMenu = oMenuMngr.GetMenu( oMenu.sMenuName )
    oFBMenu.OnMenuActivate.Add( EventMenu )
    oMenu.oFBMenu = oFBMenu

    # create menu contents (MenuItems) for tools
    print ('create tool: '+ oMenu.sMenuName)
    for i in range(len(oMenu.lmTools)):
        oTool = oMenu.lmTools[i][1]
        if hasattr( oTool, 'sToolFullName' ):
            sName = oTool.sToolFullName
            sName = oMenu.lmTools[i][0]
        oFBMenu.InsertLast( sName, i ) # you need the "i"ndex for the event function below (although you could use a string instead)

As you can see, a FBGenericMenu is stored in MenuData.oFBMenu. You will need it for the EventMenu function. After that we just iterate through each stored module, creating a menu entry for each one. If the module has a sToolFullName, we use that one for the menu entry, otherwise we use its folder name.


All menu entries call just one function. You don’t want to generate a new function for each menu entry, otherwise you will end up with some heavy meta-python coding. And you shouldn’t need to. EventMenu is called with two variables, control and event. Control is the actual FBGenericMenu that was clicked. Event.Id has the index which you used to add that menu entry, last line of the code snippet above. You also have a Event.Name, which is the actual string being displayed in the interface. With this cards in your pocket you can do this:

def EventMenu( control, event ):
    Runs the script selected from the menu. Each menu entry has a corresponding tool __init__ialized (but not loaded in memory)
    This is stored in MenuData.lmTools[1], and it should have variables/attributes point what should be imported and
    how the tool should be executed. Defaults to main() in /Scripts/MENU/TOOL/
    #print control, event.Id, event.Name

    # find which menu was clicked and get it's MenuData from loMenus
    oMenu = None
    for oTM in loMenus:
        if control == oTM.oFBMenu:
            oMenu = oTM

    sToolName = oMenu.lmTools[event.Id][0]
    oTool     = oMenu.lmTools[event.Id][1]

    # no sStartFileToImport, so we file to import is with same name as folder, as in: /Script/Tool/
    if not hasattr( oTool, 'sStartFileToImport' ):
        sModule = oMenu.sMenuName+"."+sToolName+"."+sToolName
    # with sStartFileToImport, so we have /User/Script/Tool/
        sModule = oMenu.sMenuName+"."+sToolName+'.'+oTool.sStartFileToImport

    #print "Importing:\n{0}".format(sModule)

    # if it doesn't have sCustomFunction we run main()
    if not hasattr( oTool, 'sCustomFunction' ):
        sModuleFunction = sModule + '.main()'
    # otherwise, we run the named function
        if oTool.sCustomFunction.find('(') == -1:
            sModuleFunction = sModule + '.' + oTool.sCustomFunction + '()'
            sModuleFunction = sModule + '.' + oTool.sCustomFunction

    #print "Evaluating:\n{0}".format( sModuleFunction )
    eval( sModuleFunction, sys.modules )

First, we find which main menu (the ones beside Help) the “click” happened, by comparing all MenuData.oFBMenu to control. Once we have the MenuData, we have a list of tools inside, which we can quickly fetch with event.Id, since they were added in order and each have its own unique id. Another option would be to compare strings, trying event.Name in a list of tool names, but since we allow tools to have sToolFullName and comparing string is lower than using an index directly, index is better. This is the shitty FBMenuManager part, if you don’t get how this is working you need more time playing with FBMenuManager, FBGenericMenu and FBGenericMenuItem (and/or I haven’t worded it very clearly).

Once we have the actual tool module (/Scripts/Gameplay/ImporterTool/ we can run the actual tool. I wanted to add some flexibility, so I allowed sStartFileToImport variable in the folder, which points to the actual python file that will be imported. If this variable doesn’t exists, it will try to import a python file with the same name as the folder.

Now though is when python import magic actually gets in the way.

Python “import” command runs the imported script just once. When you do import again, it’s not re-evaluating the imported code. Even if you changed the script locally, you need to do reload module instead, which can cause a series of other problems.

Instead of making self-running scripts, I wanted to also add the flexibility for a function to be called for the tool to start (say you want to run something based on selected objects, for example). That’s why it’s also checking for a sCustomFunction variable. If there’s none, it will use just run (sModule + ‘.main()’).

Lastly, it does eval. Which runs said function.

Generic menu

For the generic menu, instead of loading a module I just save the path for the script in MenuData.lmTools[(“script name”, “full path for script”)] and run them with:

execfile( oMenu.lmTools[event.Id][1], {} )

You will just need to add something in /Scripts/Generic/ (like isGenericMenu=True) so you know that that folder is a generic menu and should run another exec procedure. The empty dictionary {} at the end is to specify a new empty set of locals and globals, otherwise you get scope problems when running scripts without functions.

Leave a Comment :, , , , , , , , , more...

sharing files between 3dsMax and Motionbuilder

by on Oct.28, 2013, under 3ds Max, ini, maxscript, MotionBuilder, pipeline, python, xml

Here’s a small tip on where to quickly put files (ini or xml for example) that you can easily fetch from both Max:


And Motionbuilder:

import os

Both will return “C:\Users\USER\AppData\Local”. And if you ever wondered what’s the difference between /AppData/Local and AppData/Roaming this might shed some light on it:

Leave a Comment :, , , , , , more...

FBMenuManager, FBGenericMenu and FBGenericMenuItem

by on Aug.21, 2013, under GUI, interface, MotionBuilder, python

If this was explained somewhere in Motionbuilder’s help it would have made my day yesterday so much easier. Anyway:

FBMenuManager(): the manager
FBGenericMenu(): a menu with FBGenericMenuItem()s inside
FBGenericMenuItem(): an item inside a menu, where something happens when you click on it

You can think of FBGenericMenu as a branch with leafs inside. The FBGenericMenuItems are leafs. And with FBMenuManager() you can construct, edit and destruct more branches and leafs.

You can create leafs with FBGenericMenu, but that’s all you should use it for (when creating menus).

All main menus are FBGenericMenu instances, the ones that are besides File, Edit, Help, etc.

But here’s the important trick. If you want a submenu inside a menu you need to use FBMenuManager and specify the full path (FBGenericMenu/FBGenericMenuItem) for the new subMenu. It will then create a FBGenericMenu for your FBGenericMenuItem. Confusing? Yes.

One practical example. Say you have a menu called MyTool, and you want a submenu Import with items Animation and Character, something like:

  • MyTool
    • Import
      • Animation
      • Character

You could use this code:

oMenuMngr = FBMenuManager()

oMenuMngr.InsertLast( None, "MyTool" )
oMenuMngr.InsertLast( "MyTool", "Import" )

oMenu = oMenuMngr.GetMenu( "MyTool" )
oMenuItem = oMenu.GetFirstItem()
print oMenuItem.Caption
# Import

oMenuMngr.InsertLast( "MyTool/Import", "Animation" )
# now Import is also a FBGenericMenu as well as a FBGenericMenuItem

temp = oMenu.GetFirstItem()
print temp
# Import FBGenericMenuItem

temp = oMenuMngr.GetMenu( "MyTool/Import")
print temp
# Import FBGenericMenu

Apparently, to get a FBGenericMenu object you need the full path. The FBGenericMenuItems you can get only from the parent FBGenericMenu where it belongs.

FBGenericMenu also holds two important things:

1) a list of Ids, one integer for each FBGenereicMenuItem inside, which doesn’t need to be in order
2) OnMenuActivate, which binds all items to only one function that in turn will run each time the user click on any of FBGenericMenuItems inside this FBGenericMenu.

But I will get more to that on another post about creating dynamic menus based on tools you have on disc.

Leave a Comment :, , , , , , , , more...

exporting mirrored animations from Motionbuilder

by on Mar.06, 2011, under MotionBuilder, pipeline

It’s quite easy. Basically you need to:

  1. plot to the skeleton;
  2. save one animation;
  3. turn on Mirror Animation for the character;
  4. plot back to the control rig, which then mirrors the animation;
  5. rotate the Character Reference model 180 degrees;
  6. plot back to the skeleton;
  7. save mirrored animation.

The tricky part is just number 5 where you need to do some matrix rotation. Python for this would be something like:

from pyfbsdk import *
app = FBApplication()
char = app.CurrentCharacter
savePath = r"C:\"
filename = app.FBXFileName
skeleton = FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton
ctrlrig = FBCharacterPlotWhere.kFBCharacterPlotOnControlRig

# plot to skeleton, see bellow
plotAnim(char, skeleton)

# save left animation
sOptions = FBFbxOptions(False) # false = save options
sOptions.SaveCharacter = True
sOptions.SaveControlSet = False
sOptions.SaveCharacterExtension = False
sOptions.ShowFileDialog = False
sOptions.ShowOptionsDialog = False
app.SaveCharacterRigAndAnimation(savePath + "\\" + filename + "_L", char, sOptions)

# activate mirror and plot
char.MirrorMode = True
plotAnim(char, ctrlrig)

# get reference model
refModel = FBFindModelByName("Character_Ctrl:Reference")

# rotating 180, the tricky part
rotateY180 = FBMatrix()
rotateY180[0] = math.cos((180*0.017453292519943295769236907684886))
rotateY180[2] = math.sin((180*0.017453292519943295769236907684886))
rotateY180[8] = -math.sin((180*0.017453292519943295769236907684886))
rotateY180[10] = math.cos((180*0.017453292519943295769236907684886))

refMT = FBMatrix()

refModel.SetMatrix( MatrixMult(rotateY180, refMT) )

# plot back to skeleton
plotAnim(char, skeleton)

# save again
app.SaveCharacterRigAndAnimation(savePath + "\\" + filename + "_R", char, sOptions)

The plot and multiplication functions are:

# This is from Neil3d:
def MatrixMult(Ma, Mb):
    res = FBMatrix()

    for i in range(0,4):
        for j in range(0,4):
            for k in range(0,4):
                sum += Ma[i*4+k] * Mb[k*4+j]

            res[i*4+j] = sum
    return res

def plotAnim(char, where):
    if char.GetCharacterize:
        switchOn = char.SetCharacterizeOn(True)

    plotoBla = FBPlotOptions()
    plotoBla.ConstantKeyReducerKeepOneKey = True
    plotoBla.PlotAllTakes = True
    plotoBla.PlotOnFrame = True
    plotoBla.PlotPeriod = FBTime( 0, 0, 0, 1 )
    #plotoBla.PlotTranslationOnRootOnly = True
    plotoBla.PreciseTimeDiscontinuities = True
    #plotoBla.RotationFilterToApply = FBRotationFilter.kFBRotationFilterGimbleKiller
    plotoBla.UseConstantKeyReducer = False
    plotoBla.ConstantKeyReducerKeepOneKey  = True

    if (not char.PlotAnimation(where, plotoBla)):
        FBMessageBox( "Something went wrong", "Plot animation returned false, cannot continue", "OK", None, None )
        return False

    return char    

If you are exporting the character to a game, pay attention to the root node rotation. If you used it in the characterization, chances are it might also be rotated, and you might not want that to happen. You can also establish a convention on the name of the files, and easily detect if the animation currently open ends with _L or _R, and adapt the filename correctly.

FBApplication().SaveCharacterRigAndAnimation() is the equivalent of the Save Character Animation on the Character Controls window, which saves only the animation, without mesh. I prefer to use this when exporting only the animation from Motionbuilder to some other software, since it’s cleaner, faster and file sizes are smaller, but you could use any other function also.

Leave a Comment :, , , , , , more...

installing python modules on Motionbuilder

by on Feb.25, 2011, under MotionBuilder

It’s quite straight forward, once you have the correct package. Here is two places worth looking for 64 bits modules:

If you install the module it rests under C:\Python26\Lib\site-packages. You just need to copy it to a sys.path from Motionbuilder’s python. A good choice is: C:\Program Files\Autodesk\Autodesk MotionBuilder 2011 64-bit\bin\x64\python\lib\plat-win

Leave a Comment :, , , more...

changes to the additive animation script

by on Feb.24, 2011, under MotionBuilder

Made some improvements to the script. After almost blowing my brains out trying to debug again all the matrix math, it seems the additive results were not being added correctly to the control rig’s bind pose.

Anyway, I switched from getting the bind pose from the control rig for the actual “stance pose” for the bone skeleton. This is the pose stored when you “add the character”. I get it’s matrix and paste it on a frame on another layer at take03. It shouldn’t be needed if the bones have zeroed out rotations though.

From all that debugging I’ve learned a couple of things:

– How to install numpy and any other modules/libraries to Mobu’s python;
– That Mobu crashes if I try to extend a list of FBMatrix() to another list (but append works);
– That to get the stance pose I need to necessarily do a .SetMatrix then .AnimationNode.KeyCandidate(). And obviously a .GetMatrix before that to find the stance matrix.

I will post more about the modules thingy on the next days.

Leave a Comment :, , more...

retargeting animation, howto/tutorial/whatis

by on Feb.06, 2011, under Uncategorized

I think a couple more words for new animators might be helpful.

Retargeting is just the process of “copying” the animation from one skeleton to the other. As you probably know, the simple cut and paste of keyframes between characters do not work. The joints might have different names, might have different rotations, different zeroed rotations, positions, etc etc. Some real time engines are able to do this with very strict rules of how the skeletons must be made, most of the time the difference are just and only the proportions, where just the rotation of the joints (except the root node) are used between skeletons that are mostly equal.

When characters have different skeletons, even with different hierarchies, you have to use some sort of tool, like Motionbuidler. The way it does is to have two characters in the scene, and copy it from “one control rig to the other”. If you haven’t, you should familiarize yourself completely with Motionbuilder’s characterize tools, since they are essential to fully use the software. Characters in Motionbuilder can have many inputs, like an Actor from mocap software, the control rig, which is used to animate or edit the animation of a skeleton, or another character, to “retarget” the animation from this character to the current one.

Getting into more detail, to do this, you import or merge both skeleton hierarchies into one scene. You characterize each one of them, correctly. They need both to be on tpose, and ideally should have all their bones above ground (above 0 y). Refer to Motionbuilder help for this, it’s thoroughly explained there. And this is one of the tricky parts, to use the retargeter script, each animation to be imported must be on tpose on frame 0. You don’t actually need to create a control rig for each one of them, after both are characterized, you just need to select the input type of the new character to the old character, and activate it. Animation on both characters should then be synched, regardless of differences in hierarchies or proportions.

The retargeter script just automates this process, loading a folder containing .fbx or .bvh animations over the new character. It does characterize the animations if needed, but they need to follow either Motionbuilder nomenclature or 3dsMax Biped to be correctly characterized. If they are not, you can just edit the Motionbuilder one inside the script, changing the right column to match the names on your skeleton. You don’t need to change all of them, just the ones present on your skeleton (if you don’t have finger animations don’t bother, for example).

You can, for example, get all or some of the 2600+ mocap files from Carnegie Mellon University Motion Capture Database at, which has .bvh’s and .fbx’s with a tpose on frame 0, and with the retargeter script quickly retarget them over your character.

2 Comments :, , , , , , more...

retarget animation tool for MotionBuilder

by on Feb.02, 2011, under MotionBuilder

Finally added some new features, some polish on the code and updated the script on github. You can use it to retarget any amount of animations over your characters without much hassle. You can download it here.

You just need a scene with an already characterized character and a folder with animations in .fbx. The animations need to be either already characterized or not. If they are not, they need to be on a tpose on frame 0 and have either Motionbuilder (with or without a prefix) or 3dsMax Biped nomenclature. They need to be characterized to be retargeted from one control rig to the other, so that’s why it needs a tpose. For a custom bone mapping you can easilly add a new one or edit the mobu one.

I might add bvh support in the future (maybe with FBFileBatch()?) when I find more free time again. Or feel free to add it yourself.

* edit: Ok, added support for bvh, namespace, and custom skeletons. You still need to edit the bone mapping if you have custom skeleton though.

2 Comments :, , , , , more...

additive Animation for Motionbuilder

by on Jan.21, 2011, under MotionBuilder

Ok, done! Tested with EMotionFX at work and it worked great! It will probably work with both Unity, UDK, or any other animation system, but I’d love to hear from anyone that tries it out.

You can download the .py over at github, or all ziped files here. The readme files explains it all. You just need a characterized character, an animation ploted on take01, a pose ploted on take02 to be subtracted and an empty take03.

If you find any bugs or have any question, ideas or feature requests just send them my way.

Edit: added an example file using the Gremlin sample scene. Just open it and run the script. You can get a good idea of how it works by studying the content on the takes, it’s quite simple and straight forward.

Leave a Comment :, , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!