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, FBFindModelByLabelName, FBBodyNodeId, FBTime, FBTake
#

from pyfbsdk import FBApplication, FBSystem, FBFindModelByLabelName, FBBodyNodeId, FBTime, FBTake
import math
import sys
import os
import tempfile

# ************************************************************* #
#                                                               #
#                        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, rotationModels, translationModels):
    model = FBFindModelByLabelName(modelName)

    if not model == None:
        if not model.ClassName() == str("FBModel"):
            #print "0. Add to rotation model list: ", model.FullName
            rotationModels.append(model)            
            for child in model.Children:
                getHierarchy(child.LongName, rotationModels, None)

    if not translationModels == None:
        translationModels.append(model)
        #print "0. Add to translation model list: ", model.FullName
    

def getCharacterModels(character, models, transModels):
    chars = FBSystem().Scene.Characters
    count = 0
    countTrans = 0
    for char in chars:
        if char.Name == character:
            for nodeID in FBBodyNodeId.values.values():
                mod = char.GetModel(nodeID)
                if mod != None:
                    count = count + 1
                    models.append(mod)
                    if nodeID == FBBodyNodeId.kFBHipsNodeId:
                        transModels.append(mod)
                        countTrans = countTrans + 1
    #print "0. Get ", count, " models from character for rotation", character
    #print "0. Get ", countTrans, " hip models from character for translation", character, " \n"
    return

def getModel(modelName, models):
    mod = FBFindModelByLabelName(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.GetFrame()] = value
        counter = counter + 1       
    return curveData

def exportAnimData(animNodes, prefix, start, end, outData, 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
        # Export any animation below this animNode
        exportAnimData(animNode.Nodes, prefix, start, end, outData)


def PlotModels(models, start, end):    
    # Mark the members as selected and plot them.
    # For now, don't worry about backing up and restoring the current selection.
    print "1.Plot the models!"
    print "1.1. Select the models:"
    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 with plot rate 30, or say "plot for every frame"!
    print "1.2. Plot models from frame", start, "to frame", end
    period = FBTime(0,0,0,1)
    #period.SetSecondDouble( 1.0 / 30.0 ); # also works
    take.PlotTakeOnSelected(period)
                
    # Unselect the model    
    print "1.3. Unselect all ploted models!"
    for mod in models:
        mod.Selected = False


def GetRotationAnimation(models, start, end, allData):
    print "2. Rotation models has: ", len( rotationModels),  "models."
    count = 0
    for mod in models:
        if mod != None:
            nodeAnim = mod.AnimationNode
            for nodeAnimNode in nodeAnim.Nodes:
                if "Lcl Rotation" in nodeAnimNode.Name:
                    if len(nodeAnimNode.Nodes) > 0:
                        exportAnimData(nodeAnimNode.Nodes, mod.FullName + "__Rotation", start, end, allData, math.pi/180.0)
                        count = count + 1
                        #print mod.FullName
    print "2. Get valid rotation animation from: ", count, " models."

def GetTranslationAnimation(models, start, end, allData):
    print "2. Translation models has: ", len( translationModels),  "models."
    count = 0
    for mod in models:
        if mod != None:
            #print mod.FullName
            nodeAnim = mod.AnimationNode
            for nodeAnimNode in nodeAnim.Nodes:
                if "Lcl Translation" in nodeAnimNode.Name:
                    if len(nodeAnimNode.Nodes) > 0:
                        exportAnimData(nodeAnimNode.Nodes, mod.FullName + "__Translation", start, end, allData)
                        count = count + 1
                        #print mod.FullName
    print "2. Get valid translation animation from: ", count, " models."

def ExportAnimationData(outFile, allData):
    print "3. Output animation data into file: ", outFile, "\n"
    
    # Output the data.
    output = open (outFile, 'w')
    output.write('###########   Animation Data   ###########\n')
    for keyModelName,curveDict in allData.items():
        output.write('%s\n'%(keyModelName))
        #Sort curve dict
        keylist = curveDict.keys()
        keylist.sort()
        for keyFrame in keylist:
            output.write('%s: %s\n'%(keyFrame, curveDict[keyFrame]))
        output.write('\n')
    output.close()

def ExportModelsAnimation(outFile, rotationModels, translationModels, start, end):
    PlotModels( rotationModels, start, end )
    PlotModels( translationModels, start, end )

    allData = dict()
    GetRotationAnimation( rotationModels, start, end, allData )
    GetTranslationAnimation( translationModels, start, end, allData )

    ExportAnimationData( outFile, allData )


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

# Attention: This script use the StoryExample.fbx as an example, but you may want to replace the file with your own.
# Meanwhile, you should also change the parameters below according to your own file before using them.

# Specify the export parameters.
start = 120
end   = 130
inFile = os.path.realpath(os.path.abspath(os.path.join( FBSystem().ApplicationPath, "../../OpenRealitySDK/Scenes/StoryExample.fbx" )))
outFile = os.path.realpath(os.path.abspath(os.path.join( tempfile.gettempdir(), "StoryExample_output.txt" )))

# 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 in rotationModels list.  
# Additional model list, translationModels, is to contain models which should have translations exported for them.
rotationModels = list()
translationModels = 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("Skeleton_Model", rotationModels, translationModels)                        

# If exporting hierarchies, get the models in the named hierarchies.
# The root model of the hierarchy will be added to the transModels list.
getHierarchy ("Skeleton_Model:Hips", rotationModels, translationModels)     
getHierarchy ("Character_Ctrl:HipsEffector", rotationModels, translationModels)                             

# If exporting groups, get the models in the named groups.  For groups
# containing root models, add those models to the list of transModels.
getGroupMembers("KNIGHT:RightThighArmorLowBeltBuckle", rotationModels)

# If exporting sets, get the models in the named sets.  For sets
# containing root models, add those models to the list of transModels.
# Attention: Here we just show how to get SetMembers, and MyNamespace:MySet doesn't really exist.
'''
getSetMembers("MyNamespace:MySet", models)                              
'''

# Finally, add any additional models which should have translations
# and rotations exported for them.
getModel("KNIGHT:Hips", rotationModels)
getModel("KNIGHT:Hips", translationModels)
getModel("Character_Ctrl:HipsEffector", translationModels)


print "0. Totally get", len(rotationModels), "models to export their rotation."
#for model in rotationModels:
#    print model.FullName
print "0. Totally get", len(translationModels), "models to export their translations."
#for model in translationModels:
#    print model.FullName

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

# Export the animation for the given list of models for the given takes.
ExportModelsAnimation(outFile, rotationModels, translationModels, start, end)

print "The animation data for from frame ", start, " to frame ", end, " have been exported to file: \n", outFile