Tasks/ExportAnimationLibrary.py

# Copyright 2009 Autodesk, Inc.  All rights reserved.
# Use of this software is subject to the terms of the Autodesk license agreement 
# provided at the time of installation or download, or which otherwise accompanies
# this software in either electronic or hard copy form.
#
# This script is to demonstrate Export the animation for list of models for the given take.
#
# Topic: FBApplication, FBSystem, FBFindModelByName, FBBodyNodeId, FBTime, FBTake
#

from pyfbsdk import FBApplication, FBSystem, FBFindModelByName, FBBodyNodeId, FBTime, FBTake
import math
import sys

# ************************************************************* #
#                                                               #
#                        Export Library                         #
#                                                               #
# ************************************************************* #
  
def loadScene(sceneFile):
    application = FBApplication()
    if (not application.FileOpen(sceneFile, False)):
        return False
    return True
    
def getFullPrefix(name):
  if len( name.split(":") )>1:
    lastPref = name.split(":")[-2]
    firstPref = name.split(lastPref)[0]
    fullPrefix = firstPref+lastPref 
    return fullPrefix 
  else:
    return None

def addAllGroupMembers(group, members):
    for member in group.Items:
        if member.ClassName() == str("FBGroup"):
            addAllGroupMembers(member, members)
        else:
            members.append(member)

def addAllSetMembers(set, members):
    for member in set.Items:
        if member.ClassName() == str("FBGroup"):
            addAllGroupMembers(member, members)
        elif member.ClassName() == str("FBSet"):
            addAllSetMembers(member, members)
        else:
            members.append(member)

def getGroupMembers(groupName, members):
    for group in FBSystem().Scene.Groups:
        if group.Name == groupName.split(":")[-1]:
            for item in group.Items:
                if not getFullPrefix(item.LongName):
                    continue
                elif getFullPrefix(item.LongName) == groupName.split(":")[-2]:
                    addAllGroupMembers(group, members)
                    return
                else:
                    members.append(item)
    return

def getSetMembers(setName, members):
    for set in FBSystem().Scene.Sets:
        if set.Name == setName.split(":")[-1]:
            for item in set.Items:
                if not getFullPrefix(item.LongName):
                    continue
                elif getFullPrefix(item.LongName) == setName.split(":")[-2]:
                    addAllSetMembers(set, members)
                    return
    return

def getHierarchy (modelName, models, transModels):
  model = FBFindModelByName(modelName)
    
  if not model == None:
    if not model.ClassName() == str("FBModel"):
      models.append(model)
      
      # If this is the original call from the user, append the model
      # to the transModels list.  Pass None to successive calls.
      if not transModels == None:
        transModels.append(model)
    for child in model.Children:
      getHierarchy(child.LongName, models, None)
      
def getCharacterModels(character, models, transModels):
    chars = FBSystem().Scene.Characters
    for char in chars:
        if char.Name == character:
            for nodeID in FBBodyNodeId.values.values():
                mod = char.GetModel(nodeID)
                if mod != None:
                    models.append(mod)
                    if nodeID == FBBodyNodeId.kFBHipsNodeId:
                        transModels.append(mod)
            return

def getModel(modelName, models):
    mod = FBFindModelByName(modelName)
    if not mod == None:
        models.append(mod)

def mineCurve(start, end, fcurve, modifier):
  curveData = dict()
  counter = start
  while counter <= end:
    frameTime = FBTime(0,0,0,counter)
    value = fcurve.Evaluate(frameTime) * modifier
    curveData[frameTime.GetSecondDouble()] = value
    counter = counter + 1
  return curveData

def exportAnimData(animNodes, prefix, animDataType, start, end, outData, outDataType, modifier = 1.0):
    if animNodes == None:
        return
        
    for animNode in animNodes:
        curveName = prefix + animNode.Name
        curveData = mineCurve(start, end, animNode.FCurve, modifier)
        outData[curveName] =  curveData
        outDataType[curveName] = animDataType

        # Export any animation below this animNode
        exportAnimData(animNode.Nodes, prefix, animDataType, start, end, outData, outDataType)
        
def exportModels(outFile, models, transModels, startFrame, endFrame):
    # Export the animation for the given list of models.
    # Use the given take and export the animation over the given frame range
    
    # Mark the members as selected and plot them.
    # For now, don't worry about backing up and restoring the current selection.
    for mod in models:
        mod.Selected = True
        
    take = FBSystem().CurrentTake
    # Set the current layer to the Base Layer.
    # NOTE:  This will crash MotionBuilder for versions prior to 2009.g.  In version 2010,
    # the Base Layer index has been changed to 0 instead of -1.
    take.SetCurrentLayer(0)
    
    # Perform the plot of all the selected models.
    period = FBTime(0, 0, 0, 1)
    take.PlotTakeOnSelected(period)
    for mod in models:
        mod.Selected = False
    
    # Initialize the dictionaries that will contain the data to export.
    allData = dict()
    dataType = dict()

    for mod in models:
        if mod != None:
            print mod.LongName
            nodeAnim = mod.AnimationNode
            for nodeAnimNode in nodeAnim.Nodes:
                if "Lcl Rotation" in nodeAnimNode.Name:
                   if len(nodeAnimNode.Nodes) > 0:
                       exportAnimData(nodeAnimNode.Nodes, mod.Name + '_rotate', 'animCurveTA', \
                               start, end, allData, dataType, math.pi/180.0)

    for mod in transModels:
        if mod != None:
            nodeAnim = mod.AnimationNode
            for nodeAnimNode in nodeAnim.Nodes:
              if "Lcl Translation" in nodeAnimNode.Name:
                  if len(nodeAnimNode.Nodes) > 0:
                      exportAnimData(nodeAnimNode.Nodes, mod.Name + '_translate', 'animCurveTL', \
                              start, end, allData, dataType)

    # Output the data.
    # NOTE:  THIS OUTPUT IS SCEA SPECIFIC.  IT SHOULD BE MADE MORE GENERIC BEFORE BEING
    # SHARED WITH A WIDER AUDIENCE.
    output = open (outFile, 'w')
    output.write('allData=%s\ndataType=%s\n' % (str(allData), str(dataType)))
    output.close()


# ************************************************************* #
#                                                               #
#                    File Specific Code                         #
#                                                               #
# ************************************************************* #

# Specify the export parameters.
start = 0
end   = 100
inFile = 'C:/Temp/myInFile.fbx'
outFile = 'C:/Temp/myOutFile.py'

# Load the scene to be exported.
if not loadScene(inFile):
    sys.stderr.write('Could not load file %s' % inFile)
    raise Exception('quitting')

# Initialize a list to contain the accumulated models.  Rotation
# information will be generated for each of these models.  Initialize an
# additional model list to contain models which should have translations
# exported for them.
models = list()
transModels = list()

# If exporting characters, get the models in the named characters.
# Characters may contain their own hips unknown at export time.
# Add any hips found in the character to the list of transModels
# so they will have translations exported for them.
getCharacterModels('MyCharacter11', models, transModels)
getCharacterModels('MyCharacter2', models, transModels)

# If exporting hierarchies, get the models in the named hierarchies.
# The root model of the hierarchy will be added to the transModels list.
getHierarchy ("MyRootNode1", models, transModels)
getHierarchy ("MyRootNode2", models, transModels)

# If exporting groups, get the models in the named groups.  For groups
# containing root models, add those models to the list of transModels.
getGroupMembers("MyNamespace:MyRotGroup1", models)
getGroupMembers("MyNamespace:MyRotGroup2", models)
getGroupMembers("MyNamespace:MyTransGroup2", transModels)

# If exporting sets, get the models in the named sets.  For sets
# containing root models, add those models to the list of transModels.
getSetMembers("MyNamespace:MyRotSet1", models)
getSetMembers("MyNamespace:MyRotSet2", models)
getSetMembers("MyNamespace:MyTransSet2", transModels)

# Finally, add any additional models which should have translations
# and rotations exported for them.
getModel("SRC", models)
getModel("myNamespace1:myTrans1", transModels)
getModel("DST", models)
getModel("myNamespace2:myTrans2", transModels)

# If no models have been found, raise an exception
if len(models) == 0:
  sys.stderr.write('No models were found.')
  raise Exception('quitting')

# Export the animation for the given list of models for the given take.
exportModels(outFile, models, transModels, start, end)