Samples/Character/CharacterMarkerSetFromActor.py
 
 
 
Samples/Character/CharacterMarkerSetFromActor.py
# Copyright 2012 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.
#
# Script description:
# Advanced script showing how to convert actor solve into character marker set.
# You will find in here:
#       - how to create skeleton from actor
#       - how to create character and characterize skeleton for it
#       - how to create character marker set
#       - how to query marker setup from actor and apply it on character marker set
#       - how to change solvers on character
#
# Topic: FBCharacter FBCharacterMarkerSet FBActor
#

from pyfbsdk import *

'''
# helper functions
'''
lSkeletonNodeParentIds = {
    FBSkeletonNodeId.kFBSkeletonReferenceIndex : FBSkeletonNodeId.kFBSkeletonInvalidIndex,
    FBSkeletonNodeId.kFBSkeletonHipsIndex : FBSkeletonNodeId.kFBSkeletonReferenceIndex, 
    FBSkeletonNodeId.kFBSkeletonLeftHipIndex : FBSkeletonNodeId.kFBSkeletonHipsIndex,
    FBSkeletonNodeId.kFBSkeletonLeftKneeIndex : FBSkeletonNodeId.kFBSkeletonLeftHipIndex,
    FBSkeletonNodeId.kFBSkeletonLeftAnkleIndex : FBSkeletonNodeId.kFBSkeletonLeftKneeIndex,
    FBSkeletonNodeId.kFBSkeletonLeftFootIndex : FBSkeletonNodeId.kFBSkeletonLeftAnkleIndex,
    FBSkeletonNodeId.kFBSkeletonRightHipIndex : FBSkeletonNodeId.kFBSkeletonHipsIndex,
    FBSkeletonNodeId.kFBSkeletonRightKneeIndex : FBSkeletonNodeId.kFBSkeletonRightHipIndex,
    FBSkeletonNodeId.kFBSkeletonRightAnkleIndex : FBSkeletonNodeId.kFBSkeletonRightKneeIndex,
    FBSkeletonNodeId.kFBSkeletonRightFootIndex : FBSkeletonNodeId.kFBSkeletonRightAnkleIndex,
    FBSkeletonNodeId.kFBSkeletonWaistIndex : FBSkeletonNodeId.kFBSkeletonHipsIndex,
    FBSkeletonNodeId.kFBSkeletonChestIndex : FBSkeletonNodeId.kFBSkeletonWaistIndex,
    FBSkeletonNodeId.kFBSkeletonLeftCollarIndex : FBSkeletonNodeId.kFBSkeletonChestIndex,
    FBSkeletonNodeId.kFBSkeletonLeftShoulderIndex : FBSkeletonNodeId.kFBSkeletonLeftCollarIndex,
    FBSkeletonNodeId.kFBSkeletonLeftElbowIndex : FBSkeletonNodeId.kFBSkeletonLeftShoulderIndex,
    FBSkeletonNodeId.kFBSkeletonLeftWristIndex : FBSkeletonNodeId.kFBSkeletonLeftElbowIndex,
    FBSkeletonNodeId.kFBSkeletonRightCollarIndex : FBSkeletonNodeId.kFBSkeletonChestIndex,
    FBSkeletonNodeId.kFBSkeletonRightShoulderIndex : FBSkeletonNodeId.kFBSkeletonRightCollarIndex,
    FBSkeletonNodeId.kFBSkeletonRightElbowIndex : FBSkeletonNodeId.kFBSkeletonRightShoulderIndex,
    FBSkeletonNodeId.kFBSkeletonRightWristIndex : FBSkeletonNodeId.kFBSkeletonRightElbowIndex,
    FBSkeletonNodeId.kFBSkeletonNeckIndex : FBSkeletonNodeId.kFBSkeletonChestIndex,
    FBSkeletonNodeId.kFBSkeletonHeadIndex : FBSkeletonNodeId.kFBSkeletonNeckIndex,
    FBSkeletonNodeId.kFBSkeletonLeftThumbAIndex : FBSkeletonNodeId.kFBSkeletonLeftWristIndex,
    FBSkeletonNodeId.kFBSkeletonLeftThumbBIndex : FBSkeletonNodeId.kFBSkeletonLeftThumbAIndex,
    FBSkeletonNodeId.kFBSkeletonLeftThumbCIndex : FBSkeletonNodeId.kFBSkeletonLeftThumbBIndex,
    FBSkeletonNodeId.kFBSkeletonLeftIndexAIndex : FBSkeletonNodeId.kFBSkeletonLeftWristIndex,
    FBSkeletonNodeId.kFBSkeletonLeftIndexBIndex : FBSkeletonNodeId.kFBSkeletonLeftIndexAIndex,
    FBSkeletonNodeId.kFBSkeletonLeftIndexCIndex : FBSkeletonNodeId.kFBSkeletonLeftIndexBIndex,
    FBSkeletonNodeId.kFBSkeletonLeftMiddleAIndex : FBSkeletonNodeId.kFBSkeletonLeftWristIndex,
    FBSkeletonNodeId.kFBSkeletonLeftMiddleBIndex : FBSkeletonNodeId.kFBSkeletonLeftMiddleAIndex,
    FBSkeletonNodeId.kFBSkeletonLeftMiddleCIndex : FBSkeletonNodeId.kFBSkeletonLeftMiddleBIndex,
    FBSkeletonNodeId.kFBSkeletonLeftRingAIndex : FBSkeletonNodeId.kFBSkeletonLeftWristIndex,
    FBSkeletonNodeId.kFBSkeletonLeftRingBIndex : FBSkeletonNodeId.kFBSkeletonLeftRingAIndex,
    FBSkeletonNodeId.kFBSkeletonLeftRingCIndex : FBSkeletonNodeId.kFBSkeletonLeftRingBIndex,
    FBSkeletonNodeId.kFBSkeletonLeftPinkyAIndex : FBSkeletonNodeId.kFBSkeletonLeftWristIndex,
    FBSkeletonNodeId.kFBSkeletonLeftPinkyBIndex : FBSkeletonNodeId.kFBSkeletonLeftPinkyAIndex,
    FBSkeletonNodeId.kFBSkeletonLeftPinkyCIndex : FBSkeletonNodeId.kFBSkeletonLeftPinkyBIndex,
    FBSkeletonNodeId.kFBSkeletonRightThumbAIndex : FBSkeletonNodeId.kFBSkeletonRightWristIndex,
    FBSkeletonNodeId.kFBSkeletonRightThumbBIndex : FBSkeletonNodeId.kFBSkeletonRightThumbAIndex,
    FBSkeletonNodeId.kFBSkeletonRightThumbCIndex : FBSkeletonNodeId.kFBSkeletonRightThumbBIndex,
    FBSkeletonNodeId.kFBSkeletonRightIndexAIndex : FBSkeletonNodeId.kFBSkeletonRightWristIndex,
    FBSkeletonNodeId.kFBSkeletonRightIndexBIndex : FBSkeletonNodeId.kFBSkeletonRightIndexAIndex,
    FBSkeletonNodeId.kFBSkeletonRightIndexCIndex : FBSkeletonNodeId.kFBSkeletonRightIndexBIndex,
    FBSkeletonNodeId.kFBSkeletonRightMiddleAIndex : FBSkeletonNodeId.kFBSkeletonRightWristIndex,
    FBSkeletonNodeId.kFBSkeletonRightMiddleBIndex : FBSkeletonNodeId.kFBSkeletonRightMiddleAIndex,
    FBSkeletonNodeId.kFBSkeletonRightMiddleCIndex : FBSkeletonNodeId.kFBSkeletonRightMiddleBIndex,
    FBSkeletonNodeId.kFBSkeletonRightRingAIndex : FBSkeletonNodeId.kFBSkeletonRightWristIndex,
    FBSkeletonNodeId.kFBSkeletonRightRingBIndex : FBSkeletonNodeId.kFBSkeletonRightRingAIndex,
    FBSkeletonNodeId.kFBSkeletonRightRingCIndex : FBSkeletonNodeId.kFBSkeletonRightRingBIndex,
    FBSkeletonNodeId.kFBSkeletonRightPinkyAIndex : FBSkeletonNodeId.kFBSkeletonRightWristIndex,
    FBSkeletonNodeId.kFBSkeletonRightPinkyBIndex : FBSkeletonNodeId.kFBSkeletonRightPinkyAIndex,
    FBSkeletonNodeId.kFBSkeletonRightPinkyCIndex : FBSkeletonNodeId.kFBSkeletonRightPinkyBIndex
    };

lSkeletonNodeCharacterizationMapping = {
    FBSkeletonNodeId.kFBSkeletonReferenceIndex : "ReferenceLink",
    FBSkeletonNodeId.kFBSkeletonHipsIndex : "HipsLink", 
    FBSkeletonNodeId.kFBSkeletonLeftHipIndex : "LeftUpLegLink",
    FBSkeletonNodeId.kFBSkeletonLeftKneeIndex : "LeftLegLink",
    FBSkeletonNodeId.kFBSkeletonLeftAnkleIndex : "LeftFootLink",
    FBSkeletonNodeId.kFBSkeletonLeftFootIndex : "LeftToeBaseLink",
    FBSkeletonNodeId.kFBSkeletonRightHipIndex : "RightUpLegLink",
    FBSkeletonNodeId.kFBSkeletonRightKneeIndex : "RightLegLink",
    FBSkeletonNodeId.kFBSkeletonRightAnkleIndex : "RightFootLink",
    FBSkeletonNodeId.kFBSkeletonRightFootIndex : "RightToeBaseLink",
    FBSkeletonNodeId.kFBSkeletonWaistIndex : "SpineLink",
    FBSkeletonNodeId.kFBSkeletonChestIndex : "Spine1Link",
    FBSkeletonNodeId.kFBSkeletonLeftCollarIndex : "LeftShoulderLink",
    FBSkeletonNodeId.kFBSkeletonLeftShoulderIndex : "LeftArmLink",
    FBSkeletonNodeId.kFBSkeletonLeftElbowIndex : "LeftForeArmLink",
    FBSkeletonNodeId.kFBSkeletonLeftWristIndex : "LeftHandLink",
    FBSkeletonNodeId.kFBSkeletonRightCollarIndex : "RightShoulderLink",
    FBSkeletonNodeId.kFBSkeletonRightShoulderIndex : "RightArmLink",
    FBSkeletonNodeId.kFBSkeletonRightElbowIndex : "RightForeArmLink",
    FBSkeletonNodeId.kFBSkeletonRightWristIndex : "RightHandLink",
    FBSkeletonNodeId.kFBSkeletonNeckIndex : "NeckLink",
    FBSkeletonNodeId.kFBSkeletonHeadIndex : "HeadLink",
    FBSkeletonNodeId.kFBSkeletonLeftThumbAIndex : "LeftHandThumb1Link",
    FBSkeletonNodeId.kFBSkeletonLeftThumbBIndex : "LeftHandThumb2Link",
    FBSkeletonNodeId.kFBSkeletonLeftThumbCIndex : "LeftHandThumb3Link",
    FBSkeletonNodeId.kFBSkeletonLeftIndexAIndex : "LeftHandIndex1Link",
    FBSkeletonNodeId.kFBSkeletonLeftIndexBIndex : "LeftHandIndex2Link",
    FBSkeletonNodeId.kFBSkeletonLeftIndexCIndex : "LeftHandIndex3Link",
    FBSkeletonNodeId.kFBSkeletonLeftMiddleAIndex : "LeftHandMiddle1Link",
    FBSkeletonNodeId.kFBSkeletonLeftMiddleBIndex : "LeftHandMiddle2Link",
    FBSkeletonNodeId.kFBSkeletonLeftMiddleCIndex : "LeftHandMiddle3Link",
    FBSkeletonNodeId.kFBSkeletonLeftRingAIndex : "LeftHandRing1Link",
    FBSkeletonNodeId.kFBSkeletonLeftRingBIndex : "LeftHandRing2Link",
    FBSkeletonNodeId.kFBSkeletonLeftRingCIndex : "LeftHandRing3Link",
    FBSkeletonNodeId.kFBSkeletonLeftPinkyAIndex : "LeftHandPinky1Link",
    FBSkeletonNodeId.kFBSkeletonLeftPinkyBIndex : "LeftHandPinky2Link",
    FBSkeletonNodeId.kFBSkeletonLeftPinkyCIndex : "LeftHandPinky3Link",
    FBSkeletonNodeId.kFBSkeletonRightThumbAIndex : "RightHandThumb1Link",
    FBSkeletonNodeId.kFBSkeletonRightThumbBIndex : "RightHandThumb2Link",
    FBSkeletonNodeId.kFBSkeletonRightThumbCIndex : "RightHandThumb3Link",
    FBSkeletonNodeId.kFBSkeletonRightIndexAIndex : "RightHandIndex1Link",
    FBSkeletonNodeId.kFBSkeletonRightIndexBIndex : "RightHandIndex2Link",
    FBSkeletonNodeId.kFBSkeletonRightIndexCIndex : "RightHandIndex3Link",
    FBSkeletonNodeId.kFBSkeletonRightMiddleAIndex : "RightHandMiddle1Link",
    FBSkeletonNodeId.kFBSkeletonRightMiddleBIndex : "RightHandMiddle2Link",
    FBSkeletonNodeId.kFBSkeletonRightMiddleCIndex : "RightHandMiddle3Link",
    FBSkeletonNodeId.kFBSkeletonRightRingAIndex : "RightHandRing1Link",
    FBSkeletonNodeId.kFBSkeletonRightRingBIndex : "RightHandRing2Link",
    FBSkeletonNodeId.kFBSkeletonRightRingCIndex : "RightHandRing3Link",
    FBSkeletonNodeId.kFBSkeletonRightPinkyAIndex : "RightHandPinky1Link",
    FBSkeletonNodeId.kFBSkeletonRightPinkyBIndex : "RightHandPinky2Link",
    FBSkeletonNodeId.kFBSkeletonRightPinkyCIndex : "RightHandPinky3Link",
    };

def dic_find_key(dic, val):
    return [k for k, v in dic.iteritems() if v == val][0]

def replace_end(pString, pOldEnd, pNewEnd, pTestForExistence=False):
    if pTestForExistence == False or pString.endswith(pOldEnd):
        return pString[:(len(pString) - len(pOldEnd))] + pNewEnd
    else:
        return pString

def GetFBBodyNodeIdFromFBSkeletonId(pIndex):
    lName = dic_find_key(FBSkeletonNodeId.names, pIndex).replace('kFBSkeleton','kFB')
    lName = lName[:(len(lName) - len('Index'))] + 'NodeId'
    lBodyId = FBBodyNodeId.names[lName]
    return lBodyId

def GetFBEffectorIdFromFBSkeletonId(pIndex):
    lName = dic_find_key(FBSkeletonNodeId.names, pIndex).replace('kFBSkeleton','kFB')
    lName = lName[:(len(lName) - len('Index'))] + 'EffectorId'
    
    lEffectorId = None
    
    for k, v in FBEffectorId.names.iteritems():
        if k == lName:
            lEffectorId = v
            break
    
    #special case for waist and chest
    if lEffectorId == None:
        if FBSkeletonNodeId.values[pIndex] == FBSkeletonNodeId.kFBSkeletonWaistIndex:
            lEffectorId = FBEffectorId.kFBChestOriginEffectorId
        elif FBSkeletonNodeId.values[pIndex] == FBSkeletonNodeId.kFBSkeletonChestIndex:
            lEffectorId = FBEffectorId.kFBChestEndEffectorId
    
    return lEffectorId

def GetActorNodeNameFromFBSkeletonId(pIndex, pPrefix):
    lName = dic_find_key(FBSkeletonNodeId.names, pIndex).replace('kFBSkeleton',pPrefix)
    lName = replace_end(lName, 'AIndex', '1', True)
    lName = replace_end(lName, 'BIndex', '2', True)
    lName = replace_end(lName, 'CIndex', '3', True)
    lName = replace_end(lName, 'Index', '', True)
    return lName

def GetFBSkeletonNodeIdName(pIndex, pPrefix):
    lName = dic_find_key(FBSkeletonNodeId.names, pIndex).replace('kFBSkeleton',pPrefix)
    return lName[:(len(lName) - len('Index'))]

def GetFBSkeletonNodeIdParentId(pIndex):
    return lSkeletonNodeParentIds[FBSkeletonNodeId.values[pIndex]]

def IsValidSkeletonNodeId(pIndex):
    return pIndex >= 0 and pIndex < FBSkeletonNodeId.kFBSkeletonLastIndex

def GetOrCreateSkeletonNode(pIndex,pPrefix,pCreateIfNotExisting=True):
    if IsValidSkeletonNodeId(pIndex) == False:
        return None
    
    lName = GetFBSkeletonNodeIdName(pIndex,pPrefix)
    
    lSkeletonNode = FBFindObjectByFullName('Model::'+lName)
    if lSkeletonNode == None and pCreateIfNotExisting:
        if FBSkeletonNodeId.values[pIndex] != FBSkeletonNodeId.kFBSkeletonReferenceIndex:
            lSkeletonNode = FBModelSkeleton(lName)
        else:
            lSkeletonNode = FBModelNull(lName)
        lSkeletonNode.Show = True
    
    return lSkeletonNode

def GetActorBonesModel(pIndex):
    if IsValidSkeletonNodeId(pIndex) == False:
        return None
        
    lName = GetActorNodeNameFromFBSkeletonId(pIndex,'ActorBodyBone:')
    lActorBoneModel = FBFindObjectByFullName('Model::'+lName)
    
    return lActorBoneModel

'''
# end of helper functions
'''

# Set new external solver for character
def SetSolver(pCharacter, pSolverName):
    # remove solver
    for lComp in pCharacter.Components:
        if lComp.Name.startswith('HIK'):
            lComp.FBDelete()
            break
    # find index of solver
    lSolverNames = FBCharacterSolver.GetRegisteredSolverNames()
    for lSoverIndex in range(len(lSolverNames)):
        if lSolverNames[lSoverIndex] == pSolverName:
            pCharacter.SetExternalSolverWithIndex(lSoverIndex)

# Map skeleton model to character characterization slot
def MapSkeletonBone(pCharacter, pIndex, pPrefix):
    lModel = GetOrCreateSkeletonNode(pIndex,pPrefix)
    
    if lModel != None and pCharacter != None:
        lMappingPropName = lSkeletonNodeCharacterizationMapping[FBSkeletonNodeId.values[pIndex]]
        lMappingProp = pCharacter.PropertyList.Find(lMappingPropName,False)
        if lMappingProp != None:
            lMappingProp.ConnectSrc(lModel)
        else:
            print 'Could not characterize: ' + lModel.Name + '; MappingName: ' + lMappingPropName

# Get default extraction for character marker set (no magic in here, simply hard coded)
def GetDefaultExtraction(pNodeId, pMarkerCount):
    if pMarkerCount >= 3:
        if  pNodeId == FBBodyNodeId.kFBHipsNodeId or pNodeId == FBBodyNodeId.kFBChestNodeId or pNodeId == FBBodyNodeId.kFBHeadNodeId:
            return 0
        if pNodeId == FBBodyNodeId.kFBLeftWristNodeId or pNodeId == FBBodyNodeId.kFBRightWristNodeId:
            return 0
        if pNodeId == FBBodyNodeId.kFBLeftAnkleNodeId or pNodeId == FBBodyNodeId.kFBRightAnkleNodeId:
            return 0
    return 1

# Copy marker assignment in from source to destination if destination is empty
def CopyAssignmentIfEmpty(pCharacterMarkerSet, pDstBodyNodeId, pSrdBodyNodeId, pExtractionToSet=1):
    lDstMarkersProp = pCharacterMarkerSet.GetMarkersProperty(pDstBodyNodeId)
    
    if lDstMarkersProp != None and lDstMarkersProp.GetSrcCount() == 0:
        lSrcMarkersProp = pCharacterMarkerSet.GetMarkersProperty(pSrdBodyNodeId)
        
        if lSrcMarkersProp != None:
            lDstMarkersProp.BeginChange()
            
            for lSrcIndex in range(lSrcMarkersProp.GetSrcCount()):
                lDstMarkersProp.ConnectSrc(lSrcMarkersProp.GetSrc(lSrcIndex))
            
            lDstExtractionProp = pCharacterMarkerSet.GetExtractionProperty(pDstBodyNodeId)
            if lDstExtractionProp != None:
                lDstExtractionProp.Data = pExtractionToSet
                
            lDstMarkersProp.EndChange()
            
            print "Override done for: " + str(pDstBodyNodeId)

# Create skeleton from actor current state (with stance rotations)
def CreateSkeletonForActor(pActor,pPrefix):
    lState = pActor.GetCurrentSkeletonState(True)
    lGX = FBMatrix()
    
    for enum in FBSkeletonNodeId.values:
        if IsValidSkeletonNodeId(enum):
            lState.GetNodeMatrix(FBSkeletonNodeId.values[enum],lGX)
                     
            lSkeletonNode = GetOrCreateSkeletonNode(enum,pPrefix)
            
            lParentId = GetFBSkeletonNodeIdParentId(enum)
            lParentSkeletonNode = GetOrCreateSkeletonNode(lParentId,pPrefix)

            lSkeletonNode.SetMatrix(lGX)
            
            if lParentSkeletonNode != None:
                lSkeletonNode.Parent = lParentSkeletonNode

# Apply actpr state on characterized character
def ApplyStateFor(pCharacter,pState):
    lGX = FBMatrix()
    
    for enum in FBSkeletonNodeId.values:
        if IsValidSkeletonNodeId(enum) and enum != FBSkeletonNodeId.kFBSkeletonReferenceIndex:
            pState.GetNodeMatrix(FBSkeletonNodeId.values[enum],lGX) 
            lBodyNodeId = GetFBBodyNodeIdFromFBSkeletonId(enum)
             
            lSkeletonNode = pCharacter.GetModel(lBodyNodeId)
            lSkeletonNode.SetMatrix(lGX)
            
    FBSystem().Scene.Evaluate()

# Create characterized character from actor      
def CharacterizeActorSkeleton(pPrefix):
    lCharacter = FBCharacter(pPrefix+'Character')
    # Character marker set is supported on default MoBu solver or HIK 2014 and above.
    SetSolver(lCharacter,'HIK 2014 Solver')
    
    for enum in FBSkeletonNodeId.values:
        if IsValidSkeletonNodeId(enum):
            MapSkeletonBone(lCharacter, enum, pPrefix)
            
    lCharacter.SetCharacterizeOn(True)
    FBSystem().Scene.Evaluate()
    
    return lCharacter            

# Create Character Marker Set input for pCharDst and copy marker assignment from pActorSrc
def CopyMarkerMappingFromActorToCharacter(pCharDst, pActorSrc):
    # Get marker set from actor to get access to mapping
    lSrc = pActorSrc.MarkerSet
    # Create Marker Set
    pCharDst.CreateCharacterMarkerSet(False)
    # Get pointer to newly created marker set (force get, since it's not active)
    lDst = pCharDst.GetCharacterMarkerSet(True)
    
    # Stop refreshes
    FBBeginChangeAllModels()
    
    # For every possible skeleton node
    for enum in FBSkeletonNodeId.values:
        # If it's valid, copy marker assignment
        if IsValidSkeletonNodeId(enum) and enum != FBSkeletonNodeId.kFBSkeletonReferenceIndex:
            lSkID = FBSkeletonNodeId.values[enum]
            lAssignMarkers = []
            
            for lMarkerIndex in range(lSrc.GetMarkerCount(lSkID)):
                if lSrc.GetMarkerUsed(lSkID, lMarkerIndex) == False:
                    continue
                
                if lSrc.GetMarkerOriented(lSkID, lMarkerIndex) == False:
                    lAssignMarkers.append(lSrc.GetMarkerModel(lSkID, lMarkerIndex))
                else:
                    print "Oriented marker set is unsupported in here"
                    
            if len(lAssignMarkers) > 0:
                lBodyNodeId = GetFBBodyNodeIdFromFBSkeletonId(enum)
                lMarkersProp = lDst.GetMarkersProperty(lBodyNodeId)
                
                if lMarkersProp != None:
                    # start transaction to perform less refresh
                    lMarkersProp.BeginChange()
                    
                    for lMarker in lAssignMarkers:
                        lMarkersProp.ConnectSrc(lMarker)
                    
                    # adjust extraction
                    lExtractionProp = lDst.GetExtractionProperty(lBodyNodeId)
                    if lExtractionProp != None:
                        lExtractionProp.Data = GetDefaultExtraction(lBodyNodeId, len(lAssignMarkers))
                    
                    lMarkersProp.EndChange()
    
    # Character Marker Set is not a "black box" like character, for better solving it require knees and elbows to be assigned
    CopyAssignmentIfEmpty(lDst, FBBodyNodeId.kFBLeftElbowNodeId, FBBodyNodeId.kFBLeftWristNodeId)
    CopyAssignmentIfEmpty(lDst, FBBodyNodeId.kFBRightElbowNodeId, FBBodyNodeId.kFBRightWristNodeId)
    CopyAssignmentIfEmpty(lDst, FBBodyNodeId.kFBLeftKneeNodeId, FBBodyNodeId.kFBLeftAnkleNodeId)
    CopyAssignmentIfEmpty(lDst, FBBodyNodeId.kFBRightKneeNodeId, FBBodyNodeId.kFBRightAnkleNodeId)
    
    # Release refresh                
    FBEndChangeAllModels()

# All in one, convert given actor to character, prefix is used to easy localize recreated skeleton models and character
def ConvertActorToCharacter(pActor, pPrefix):
    # create skeleton from actor
    CreateSkeletonForActor(pActor,pPrefix)
    
    # setup characterization and lock character
    lChar = CharacterizeActorSkeleton(pPrefix)
    
    # prepare for snap
    ApplyStateFor(lChar, pActor.GetCurrentSkeletonState(False))

    # snap markers on character marker set
    CopyMarkerMappingFromActorToCharacter(lChar, pActor)
    
    # set character marker set input
    lChar.InputType = FBCharacterInputType.kFBCharacterInputMoCap

    # Activate Input
    lChar.Active = True

# If there is actor in the scene, convert first one.
if len(FBSystem().Scene.Actors) > 0:    ConvertActorToCharacter(FBSystem().Scene.Actors[0],'ActorSkeleton:')