(filepath,
        context = bpy.context,
        oArmature = None,
        bFilenameAsPrefix = False,
        bActionsToTrack = False,
        first_frames = 0,
        bDontInvertRoot = False,
        bUpdateTimelineRange = False,
        fcurve_interpolation = 'LINEAR',
        error_callback = __pass
        )
Import animation data from 'filepath' using 'oArmature'
    
    Args:
        first_frames: (0 - import all)
            Import only 'first_frames' from each action
          
        bActionsToTrack:
            Put all imported actions in one NLAtrack.
          
        oArmature:
            Skeleton used to calculate keyframes
Import animation data from 'filepath' using 'oArmature'
    
    Args:
        first_frames: (0 - import all)
            Import only 'first_frames' from each action
          
        bActionsToTrack:
            Put all imported actions in one NLAtrack.
          
        oArmature:
            Skeleton used to calculate keyframes
Import animation data from filepath using oArmature Args : first_frames : ( 0 - import all ) Import only first_frames from each action bActionsToTrack : Put all imported actions in one NLAtrack . oArmature : Skeleton used to calculate keyframes
def psaimport(filepath,
        context = bpy.context,
        oArmature = None,
        bFilenameAsPrefix = False,
        bActionsToTrack = False,
        first_frames = 0,
        bDontInvertRoot = False,
        bUpdateTimelineRange = False,
        fcurve_interpolation = 'LINEAR',
        error_callback = __pass
        ):
    """Import animation data from 'filepath' using 'oArmature'
    
    Args:
        first_frames: (0 - import all)
            Import only 'first_frames' from each action
          
        bActionsToTrack:
            Put all imported actions in one NLAtrack.
          
        oArmature:
            Skeleton used to calculate keyframes
    """
    print ("-----------------------------------------------")
    print ("---------EXECUTING PSA PYTHON IMPORTER---------")
    print ("-----------------------------------------------")
    
    file_ext = 'psa'
    try:
        psafile = open(filepath, 'rb')
    except IOError:
        error_callback('Error while opening file for reading:\n  "'+filepath+'"')
        return False
   
    print ("Importing file: ", filepath)
    
    armature_obj = oArmature
    
    if armature_obj is None:  
        armature_obj = blen_get_armature_from_selection()
        if armature_obj is None:
            error_callback("No armature selected.")
            return False


    chunk_id = None
    chunk_type = None
    chunk_datasize = None
    chunk_datacount = None
    chunk_data = None

    def read_chunk():
        nonlocal chunk_id, chunk_type,\
                 chunk_datasize, chunk_datacount,\
                 chunk_data

        (chunk_id, chunk_type,
         chunk_datasize, chunk_datacount) = unpack('20s3i', psafile.read(32))
        
        chunk_data = psafile.read(chunk_datacount * chunk_datasize)
    #============================================================================================== 
    # General Header
    #============================================================================================== 
    read_chunk()
    
    if not util_is_header_valid(filepath, file_ext, chunk_id, error_callback):
        return False
    
    #============================================================================================== 
    # Bones (FNamedBoneBinary)
    #============================================================================================== 
    read_chunk()
        
    psa_bones = {}
    
    def new_psa_bone(bone, pose_bone):
        psa_bone = class_psa_bone()
        
        psa_bones[pose_bone.name] = psa_bone
        
        psa_bone.name = pose_bone.name
        
        psa_bone.pose_bone = pose_bone
        
        if bone.parent != None:
            # does needed parent bone was added from psa file
            if bone.parent.name in psa_bones:
                psa_bone.parent = psa_bones[bone.parent.name]
            # no. armature doesnt match
            else:
                psa_bone.parent = None
        # else:
            # psa_bone.parent = None
            
        psa_bone.orig_quat = Quaternion(bone['orig_quat'])
        psa_bone.orig_loc  =     Vector(bone['orig_loc'])
        psa_bone.post_quat = Quaternion(bone['post_quat'])
        return psa_bone
        
    #Bones Data
    BoneIndex2Name = [None] * chunk_datacount
    BoneNotFoundList = []
    BonesWithoutAnimation = []
    PsaBonesToProcess = [None] * chunk_datacount

    # printlog("Name\tFlgs\tNumChld\tPrntIdx\tQx\tQy\tQz\tQw\tLocX\tLocY\tLocZ\tLength\tXSize\tYSize\tZSize\n")

    
    # for case insensetive comparison
    # key = lowered name
    # value = orignal name
    skeleton_bones_lowered = {}
    
    for blender_bone_name in armature_obj.data.bones.keys():
      skeleton_bones_lowered[blender_bone_name.lower()] = blender_bone_name

    
    for counter in range(chunk_datacount):
        
        # tPrntIdx is -1 for parent; and 0 for other; no more useful data
        # indata = unpack_from('64s3i11f', chunk_data, chunk_datasize * counter)
        (indata) = unpack_from('64s56x', chunk_data, chunk_datasize * counter)
        in_name = util_bytes_to_str(indata[0])
        # bonename = util_bytes_to_str(indata[0]).upper()
        
        in_name_lowered = in_name.lower()
        if in_name_lowered in skeleton_bones_lowered:
            orig_name = skeleton_bones_lowered[in_name_lowered]
            
            # use a skeleton bone name 
            BoneIndex2Name[counter] = orig_name
            PsaBonesToProcess[counter] = new_psa_bone(armature_obj.data.bones[orig_name], 
                                                      armature_obj.pose.bones[orig_name])
        else:
            # print("Can't find the bone:", bonename)
            BoneNotFoundList.append(counter)
            
    
    if len(psa_bones) == 0:
        error_callback('No bone was match!\nSkip import!')
        return False
    
    # does anyone care?
    for blender_bone_name in armature_obj.data.bones.keys():
        if BoneIndex2Name.count(blender_bone_name) == 0:
            BonesWithoutAnimation.append(blender_bone_name)
            
    if len(BoneNotFoundList) > 0:
      print('Not found bones: %i.' % len(BoneNotFoundList));
      
    if len(BonesWithoutAnimation) > 0:
      print('Bones(%i) without animation data:\n' % len(BonesWithoutAnimation), ', '.join(BonesWithoutAnimation))
    #============================================================================================== 
    # Animations (AniminfoBinary)
    #============================================================================================== 
    read_chunk()

    Raw_Key_Nums = 0
    Action_List = [None] * chunk_datacount
    
    for counter in range(chunk_datacount):
        (action_name_raw,        #0
         group_name_raw,         #1
         Totalbones,             #2
         RootInclude,            #3
         KeyCompressionStyle,    #4
         KeyQuotum,              #5
         KeyReduction,           #6
         TrackTime,              #7
         AnimRate,               #8
         StartBone,              #9
         FirstRawFrame,          #10
         NumRawFrames            #11
        ) = unpack_from('64s64s4i3f3i', chunk_data, chunk_datasize * counter)
                       
        action_name = util_bytes_to_str( action_name_raw )
        group_name = util_bytes_to_str( group_name_raw  )

        Raw_Key_Nums += Totalbones * NumRawFrames
        Action_List[counter] = ( action_name, group_name, Totalbones, NumRawFrames)
        
    #============================================================================================== 
    # Raw keys (VQuatAnimKey) 3f vec, 4f quat, 1f time
    #============================================================================================== 
    read_chunk()
    
    if(Raw_Key_Nums != chunk_datacount):
        error_callback(
                'Raw_Key_Nums Inconsistent.'
                '\nData count found: '+chunk_datacount+
                '\nRaw_Key_Nums:' + Raw_Key_Nums
                )
        return False

    Raw_Key_List = [None] * chunk_datacount
    
    unpack_data = Struct('3f4f4x').unpack_from
 
    for counter in range(chunk_datacount):
        pos = Vector()
        quat = Quaternion()
        
        ( pos.x,  pos.y,  pos.z,
         quat.x, quat.y, quat.z, quat.w
        ) = unpack_data( chunk_data, chunk_datasize * counter)
        
        Raw_Key_List[counter] = (pos, quat)
    
    psafile.close()
    
    utils_set_mode('OBJECT')

    # index of current frame in raw input data
    raw_key_index = 0
    
    util_obj_set_active(context, armature_obj)
    
    gen_name_part = util_gen_name_part(filepath)
    
    armature_obj.animation_data_create()
    
    if bActionsToTrack:
        nla_track = armature_obj.animation_data.nla_tracks.new()
        nla_track.name = gen_name_part
        nla_stripes = nla_track.strips
        nla_track_last_frame = 0
    else:
        is_first_action = True
        first_action = None
        
    for counter, (Name, Group, Totalbones, NumRawFrames) in enumerate(Action_List):
        ref_time = time.process_time()
        
        if Group != 'None':
            Name = "(%s) %s" % (Group,Name)
        if bFilenameAsPrefix:
            Name = "(%s) %s" % (gen_name_part, Name)
            
        action = bpy.data.actions.new(name = Name)
        
        # force print usefull information to console(due to possible long execution)
        print("Action {0:>3d}/{1:<3d} frames: {2:>4d} {3}".format(
                counter+1, len(Action_List), NumRawFrames, Name)
              )
        
        if first_frames > 0:
            maxframes = first_frames
            keyframes = min(first_frames, NumRawFrames)
            #dev
            # keyframes += 1
        else:
            maxframes = 99999999
            keyframes = NumRawFrames
            
        # create all fcurves(for all bones) for an action
        # for pose_bone in armature_obj.pose.bones:
        for psa_bone in PsaBonesToProcess:
            if psa_bone is None:
                continue
            pose_bone = psa_bone.pose_bone
            
            data_path = pose_bone.path_from_id("rotation_quaternion")
            psa_bone.fcurve_quat_w = action.fcurves.new(data_path, index = 0)
            psa_bone.fcurve_quat_x = action.fcurves.new(data_path, index = 1)
            psa_bone.fcurve_quat_y = action.fcurves.new(data_path, index = 2)
            psa_bone.fcurve_quat_z = action.fcurves.new(data_path, index = 3)
        
            data_path = pose_bone.path_from_id("location")
            psa_bone.fcurve_loc_x = action.fcurves.new(data_path, index = 0)
            psa_bone.fcurve_loc_y = action.fcurves.new(data_path, index = 1)
            psa_bone.fcurve_loc_z = action.fcurves.new(data_path, index = 2)
            
            # 1. Pre-add keyframes! \0/
            # 2. Set data: keyframe_points[].co[0..1]
            # 3. If 2 is not done, do 4: (important!!!)
            # 4. "Apply" data: fcurve.update()
            # added keyframes points by default is breaking fcurve somehow
            # bcs they are all at the same position?
            psa_bone.fcurve_quat_w.keyframe_points.add(keyframes)
            psa_bone.fcurve_quat_x.keyframe_points.add(keyframes)
            psa_bone.fcurve_quat_y.keyframe_points.add(keyframes)
            psa_bone.fcurve_quat_z.keyframe_points.add(keyframes)

            psa_bone.fcurve_loc_x.keyframe_points.add(keyframes) 
            psa_bone.fcurve_loc_y.keyframe_points.add(keyframes) 
            psa_bone.fcurve_loc_z.keyframe_points.add(keyframes) 
            
        for i in range(0,min(maxframes, NumRawFrames)):
            # raw_key_index+= Totalbones * 5 #55
            for j in range(Totalbones):
                if j in BoneNotFoundList:
                    raw_key_index += 1
                    continue
                
                psa_bone = PsaBonesToProcess[j]
                pose_bone = psa_bone.pose_bone
                
                p_pos = Raw_Key_List[raw_key_index][0]
                p_quat = Raw_Key_List[raw_key_index][1]
                
                ##### Worked with no bone rotation
                # quat = p_quat.conjugated() * psa_bone.orig_quat
                # loc = p_pos - psa_bone.orig_loc
                #####
                    

                if psa_bone.parent:
                    ##### Correct
                    # orig_prot = pose_bone.bone.parent.matrix_local.to_3x3().to_quaternion()
                    # orig_rot = pose_bone.bone.matrix_local.to_3x3().to_quaternion()
                    # orig_rot = (orig_prot.conjugated() * orig_rot)
                    ######

                    #### FINAL
                    quat = (p_quat * psa_bone.post_quat).conjugated() * (psa_bone.orig_quat * psa_bone.post_quat)
                    # loc = psa_bone.post_quat.conjugated() * p_pos -  psa_bone.post_quat.conjugated() * psa_bone.orig_loc
                    ####
                else:
                    if bDontInvertRoot:
                        quat = (p_quat.conjugated() * psa_bone.post_quat).conjugated() * (psa_bone.orig_quat * psa_bone.post_quat)
                    else:
                        quat = (p_quat * psa_bone.post_quat).conjugated() * (psa_bone.orig_quat * psa_bone.post_quat)
                        
                loc = psa_bone.post_quat.conjugated() * p_pos -  psa_bone.post_quat.conjugated() * psa_bone.orig_loc
                    
                pose_bone.rotation_quaternion = quat
                pose_bone.location = loc
                    # pose_bone.rotation_quaternion = orig_rot.conjugated()
                    # pose_bone.location = p_pos - (pose_bone.bone.matrix_local.translation - pose_bone.bone.parent.matrix_local.translation)
                
                ##### Works + post_quat (without location works)
                # quat = (p_quat * psa_bone.post_quat).conjugated() * (psa_bone.orig_quat * psa_bone.post_quat)
                # loc = psa_bone.post_quat.conjugated() * (p_pos - psa_bone.orig_loc)

                
                psa_bone.fcurve_quat_w.keyframe_points[i].co = i, quat.w
                psa_bone.fcurve_quat_x.keyframe_points[i].co = i, quat.x
                psa_bone.fcurve_quat_y.keyframe_points[i].co = i, quat.y
                psa_bone.fcurve_quat_z.keyframe_points[i].co = i, quat.z
                
                psa_bone.fcurve_quat_w.keyframe_points[i].interpolation = fcurve_interpolation
                psa_bone.fcurve_quat_x.keyframe_points[i].interpolation = fcurve_interpolation
                psa_bone.fcurve_quat_y.keyframe_points[i].interpolation = fcurve_interpolation
                psa_bone.fcurve_quat_z.keyframe_points[i].interpolation = fcurve_interpolation
                
                psa_bone.fcurve_loc_x.keyframe_points[i].co = i, loc.x
                psa_bone.fcurve_loc_y.keyframe_points[i].co = i, loc.y
                psa_bone.fcurve_loc_z.keyframe_points[i].co = i, loc.z
                
                psa_bone.fcurve_loc_x.keyframe_points[i].interpolation = fcurve_interpolation
                psa_bone.fcurve_loc_y.keyframe_points[i].interpolation = fcurve_interpolation
                psa_bone.fcurve_loc_z.keyframe_points[i].interpolation = fcurve_interpolation
                
                # Old path. Slower.
                # psa_bone.fcurve_quat_w.keyframe_points.insert(i,quat.w,{'NEEDED','FAST'}).interpolation = fcurve_interpolation
                # psa_bone.fcurve_quat_x.keyframe_points.insert(i,quat.x,{'NEEDED','FAST'}).interpolation = fcurve_interpolation
                # psa_bone.fcurve_quat_y.keyframe_points.insert(i,quat.y,{'NEEDED','FAST'}).interpolation = fcurve_interpolation
                # psa_bone.fcurve_quat_z.keyframe_points.insert(i,quat.z,{'NEEDED','FAST'}).interpolation = fcurve_interpolation

                # psa_bone.fcurve_loc_x.keyframe_points.insert(i,loc.x,{'NEEDED','FAST'}).interpolation = fcurve_interpolation
                # psa_bone.fcurve_loc_y.keyframe_points.insert(i,loc.y,{'NEEDED','FAST'}).interpolation = fcurve_interpolation
                # psa_bone.fcurve_loc_z.keyframe_points.insert(i,loc.z,{'NEEDED','FAST'}).interpolation = fcurve_interpolation
                raw_key_index += 1
            
            # on first frame
            # break
        raw_key_index += (NumRawFrames-min(maxframes,NumRawFrames)) * Totalbones

        # Add action to tail of the nla track
        if bActionsToTrack:
            if nla_track_last_frame == 0:
                nla_stripes.new(Name, 0, action)
            else:
                nla_stripes.new(Name, nla_stripes[-1].frame_end, action)

            nla_track_last_frame += NumRawFrames
        elif is_first_action:
            first_action = action
            is_first_action = False
            
        print("Done: %f sec." % (time.process_time() - ref_time))
        # break on first animation set
        # break
        
    scene = util_get_scene(context)
    
    if not bActionsToTrack:
        if not scene.is_nla_tweakmode:
            armature_obj.animation_data.action = first_action
    
    if bUpdateTimelineRange:

        scene.frame_start = 0

        if bActionsToTrack:
            scene.frame_end = sum(frames for _, _, _, frames in Action_List) - 1
        else:
            scene.frame_end = max(frames for _, _, _, frames in Action_List) - 1


    util_select_all(False)
    util_obj_select(context, armature_obj)
    util_obj_set_active(context, armature_obj)
    
    # 2.8 crashes
    if not is_blen_280:
        scene.frame_set(0)
def psaimport ( filepath , context = bpy . context , oArmature = None , bFilenameAsPrefix = False , bActionsToTrack = False , first_frames = 0 , bDontInvertRoot = False , bUpdateTimelineRange = False , fcurve_interpolation = 'LINEAR' , error_callback = __pass ) : print ( "-----------------------------------------------" ) print ( "---------EXECUTING PSA PYTHON IMPORTER---------" ) print ( "-----------------------------------------------" ) file_ext = 'psa' try : psafile = open ( filepath , 'rb' ) except IOError : error_callback ( 'Error while opening file for reading:\n  "' + filepath + '"' ) return False print ( "Importing file: " , filepath ) armature_obj = oArmature if armature_obj is None : armature_obj = blen_get_armature_from_selection ( ) if armature_obj is None : error_callback ( "No armature selected." ) return False chunk_id = None chunk_type = None chunk_datasize = None chunk_datacount = None chunk_data = None def read_chunk ( ) : nonlocal chunk_id , chunk_type , chunk_datasize , chunk_datacount , chunk_data ( chunk_id , chunk_type , chunk_datasize , chunk_datacount ) = unpack ( '20s3i' , psafile . read ( 32 ) ) chunk_data = psafile . read ( chunk_datacount * chunk_datasize ) #==============================================================================================  # General Header #==============================================================================================  read_chunk ( ) if not util_is_header_valid ( filepath , file_ext , chunk_id , error_callback ) : return False #==============================================================================================  # Bones (FNamedBoneBinary) #==============================================================================================  read_chunk ( ) psa_bones = { } def new_psa_bone ( bone , pose_bone ) : psa_bone = class_psa_bone ( ) psa_bones [ pose_bone . name ] = psa_bone psa_bone . name = pose_bone . name psa_bone . pose_bone = pose_bone if bone . parent != None : # does needed parent bone was added from psa file if bone . parent . name in psa_bones : psa_bone . parent = psa_bones [ bone . parent . name ] # no. armature doesnt match else : psa_bone . parent = None # else: # psa_bone.parent = None psa_bone . orig_quat = Quaternion ( bone [ 'orig_quat' ] ) psa_bone . orig_loc = Vector ( bone [ 'orig_loc' ] ) psa_bone . post_quat = Quaternion ( bone [ 'post_quat' ] ) return psa_bone #Bones Data BoneIndex2Name = [ None ] * chunk_datacount BoneNotFoundList = [ ] BonesWithoutAnimation = [ ] PsaBonesToProcess = [ None ] * chunk_datacount # printlog("Name\tFlgs\tNumChld\tPrntIdx\tQx\tQy\tQz\tQw\tLocX\tLocY\tLocZ\tLength\tXSize\tYSize\tZSize\n") # for case insensetive comparison # key = lowered name # value = orignal name skeleton_bones_lowered = { } for blender_bone_name in armature_obj . data . bones . keys ( ) : skeleton_bones_lowered [ blender_bone_name . lower ( ) ] = blender_bone_name for counter in range ( chunk_datacount ) : # tPrntIdx is -1 for parent; and 0 for other; no more useful data # indata = unpack_from('64s3i11f', chunk_data, chunk_datasize * counter) ( indata ) = unpack_from ( '64s56x' , chunk_data , chunk_datasize * counter ) in_name = util_bytes_to_str ( indata [ 0 ] ) # bonename = util_bytes_to_str(indata[0]).upper() in_name_lowered = in_name . lower ( ) if in_name_lowered in skeleton_bones_lowered : orig_name = skeleton_bones_lowered [ in_name_lowered ] # use a skeleton bone name  BoneIndex2Name [ counter ] = orig_name PsaBonesToProcess [ counter ] = new_psa_bone ( armature_obj . data . bones [ orig_name ] , armature_obj . pose . bones [ orig_name ] ) else : # print("Can't find the bone:", bonename) BoneNotFoundList . append ( counter ) if len ( psa_bones ) == 0 : error_callback ( 'No bone was match!\nSkip import!' ) return False # does anyone care? for blender_bone_name in armature_obj . data . bones . keys ( ) : if BoneIndex2Name . count ( blender_bone_name ) == 0 : BonesWithoutAnimation . append ( blender_bone_name ) if len ( BoneNotFoundList ) > 0 : print ( 'Not found bones: %i.' % len ( BoneNotFoundList ) ) if len ( BonesWithoutAnimation ) > 0 : print ( 'Bones(%i) without animation data:\n' % len ( BonesWithoutAnimation ) , ', ' . join ( BonesWithoutAnimation ) ) #==============================================================================================  # Animations (AniminfoBinary) #==============================================================================================  read_chunk ( ) Raw_Key_Nums = 0 Action_List = [ None ] * chunk_datacount for counter in range ( chunk_datacount ) : ( action_name_raw , #0 group_name_raw , #1 Totalbones , #2 RootInclude , #3 KeyCompressionStyle , #4 KeyQuotum , #5 KeyReduction , #6 TrackTime , #7 AnimRate , #8 StartBone , #9 FirstRawFrame , #10 NumRawFrames #11 ) = unpack_from ( '64s64s4i3f3i' , chunk_data , chunk_datasize * counter ) action_name = util_bytes_to_str ( action_name_raw ) group_name = util_bytes_to_str ( group_name_raw ) Raw_Key_Nums += Totalbones * NumRawFrames Action_List [ counter ] = ( action_name , group_name , Totalbones , NumRawFrames ) #==============================================================================================  # Raw keys (VQuatAnimKey) 3f vec, 4f quat, 1f time #==============================================================================================  read_chunk ( ) if ( Raw_Key_Nums != chunk_datacount ) : error_callback ( 'Raw_Key_Nums Inconsistent.' '\nData count found: ' + chunk_datacount + '\nRaw_Key_Nums:' + Raw_Key_Nums ) return False Raw_Key_List = [ None ] * chunk_datacount unpack_data = Struct ( '3f4f4x' ) . unpack_from for counter in range ( chunk_datacount ) : pos = Vector ( ) quat = Quaternion ( ) ( pos . x , pos . y , pos . z , quat . x , quat . y , quat . z , quat . w ) = unpack_data ( chunk_data , chunk_datasize * counter ) Raw_Key_List [ counter ] = ( pos , quat ) psafile . close ( ) utils_set_mode ( 'OBJECT' ) # index of current frame in raw input data raw_key_index = 0 util_obj_set_active ( context , armature_obj ) gen_name_part = util_gen_name_part ( filepath ) armature_obj . animation_data_create ( ) if bActionsToTrack : nla_track = armature_obj . animation_data . nla_tracks . new ( ) nla_track . name = gen_name_part nla_stripes = nla_track . strips nla_track_last_frame = 0 else : is_first_action = True first_action = None for counter , ( Name , Group , Totalbones , NumRawFrames ) in enumerate ( Action_List ) : ref_time = time . process_time ( ) if Group != 'None' : Name = "(%s) %s" % ( Group , Name ) if bFilenameAsPrefix : Name = "(%s) %s" % ( gen_name_part , Name ) action = bpy . data . actions . new ( name = Name ) # force print usefull information to console(due to possible long execution) print ( "Action {0:>3d}/{1:<3d} frames: {2:>4d} {3}" . format ( counter + 1 , len ( Action_List ) , NumRawFrames , Name ) ) if first_frames > 0 : maxframes = first_frames keyframes = min ( first_frames , NumRawFrames ) #dev # keyframes += 1 else : maxframes = 99999999 keyframes = NumRawFrames # create all fcurves(for all bones) for an action # for pose_bone in armature_obj.pose.bones: for psa_bone in PsaBonesToProcess : if psa_bone is None : continue pose_bone = psa_bone . pose_bone data_path = pose_bone . path_from_id ( "rotation_quaternion" ) psa_bone . fcurve_quat_w = action . fcurves . new ( data_path , index = 0 ) psa_bone . fcurve_quat_x = action . fcurves . new ( data_path , index = 1 ) psa_bone . fcurve_quat_y = action . fcurves . new ( data_path , index = 2 ) psa_bone . fcurve_quat_z = action . fcurves . new ( data_path , index = 3 ) data_path = pose_bone . path_from_id ( "location" ) psa_bone . fcurve_loc_x = action . fcurves . new ( data_path , index = 0 ) psa_bone . fcurve_loc_y = action . fcurves . new ( data_path , index = 1 ) psa_bone . fcurve_loc_z = action . fcurves . new ( data_path , index = 2 ) # 1. Pre-add keyframes! \0/ # 2. Set data: keyframe_points[].co[0..1] # 3. If 2 is not done, do 4: (important!!!) # 4. "Apply" data: fcurve.update() # added keyframes points by default is breaking fcurve somehow # bcs they are all at the same position? psa_bone . fcurve_quat_w . keyframe_points . add ( keyframes ) psa_bone . fcurve_quat_x . keyframe_points . add ( keyframes ) psa_bone . fcurve_quat_y . keyframe_points . add ( keyframes ) psa_bone . fcurve_quat_z . keyframe_points . add ( keyframes ) psa_bone . fcurve_loc_x . keyframe_points . add ( keyframes ) psa_bone . fcurve_loc_y . keyframe_points . add ( keyframes ) psa_bone . fcurve_loc_z . keyframe_points . add ( keyframes ) for i in range ( 0 , min ( maxframes , NumRawFrames ) ) : # raw_key_index+= Totalbones * 5 #55 for j in range ( Totalbones ) : if j in BoneNotFoundList : raw_key_index += 1 continue psa_bone = PsaBonesToProcess [ j ] pose_bone = psa_bone . pose_bone p_pos = Raw_Key_List [ raw_key_index ] [ 0 ] p_quat = Raw_Key_List [ raw_key_index ] [ 1 ] ##### Worked with no bone rotation # quat = p_quat.conjugated() * psa_bone.orig_quat # loc = p_pos - psa_bone.orig_loc ##### if psa_bone . parent : ##### Correct # orig_prot = pose_bone.bone.parent.matrix_local.to_3x3().to_quaternion() # orig_rot = pose_bone.bone.matrix_local.to_3x3().to_quaternion() # orig_rot = (orig_prot.conjugated() * orig_rot) ###### #### FINAL quat = ( p_quat * psa_bone . post_quat ) . conjugated ( ) * ( psa_bone . orig_quat * psa_bone . post_quat ) # loc = psa_bone.post_quat.conjugated() * p_pos -  psa_bone.post_quat.conjugated() * psa_bone.orig_loc #### else : if bDontInvertRoot : quat = ( p_quat . conjugated ( ) * psa_bone . post_quat ) . conjugated ( ) * ( psa_bone . orig_quat * psa_bone . post_quat ) else : quat = ( p_quat * psa_bone . post_quat ) . conjugated ( ) * ( psa_bone . orig_quat * psa_bone . post_quat ) loc = psa_bone . post_quat . conjugated ( ) * p_pos - psa_bone . post_quat . conjugated ( ) * psa_bone . orig_loc pose_bone . rotation_quaternion = quat pose_bone . location = loc # pose_bone.rotation_quaternion = orig_rot.conjugated() # pose_bone.location = p_pos - (pose_bone.bone.matrix_local.translation - pose_bone.bone.parent.matrix_local.translation) ##### Works + post_quat (without location works) # quat = (p_quat * psa_bone.post_quat).conjugated() * (psa_bone.orig_quat * psa_bone.post_quat) # loc = psa_bone.post_quat.conjugated() * (p_pos - psa_bone.orig_loc) psa_bone . fcurve_quat_w . keyframe_points [ i ] . co = i , quat . w psa_bone . fcurve_quat_x . keyframe_points [ i ] . co = i , quat . x psa_bone . fcurve_quat_y . keyframe_points [ i ] . co = i , quat . y psa_bone . fcurve_quat_z . keyframe_points [ i ] . co = i , quat . z psa_bone . fcurve_quat_w . keyframe_points [ i ] . interpolation = fcurve_interpolation psa_bone . fcurve_quat_x . keyframe_points [ i ] . interpolation = fcurve_interpolation psa_bone . fcurve_quat_y . keyframe_points [ i ] . interpolation = fcurve_interpolation psa_bone . fcurve_quat_z . keyframe_points [ i ] . interpolation = fcurve_interpolation psa_bone . fcurve_loc_x . keyframe_points [ i ] . co = i , loc . x psa_bone . fcurve_loc_y . keyframe_points [ i ] . co = i , loc . y psa_bone . fcurve_loc_z . keyframe_points [ i ] . co = i , loc . z psa_bone . fcurve_loc_x . keyframe_points [ i ] . interpolation = fcurve_interpolation psa_bone . fcurve_loc_y . keyframe_points [ i ] . interpolation = fcurve_interpolation psa_bone . fcurve_loc_z . keyframe_points [ i ] . interpolation = fcurve_interpolation # Old path. Slower. # psa_bone.fcurve_quat_w.keyframe_points.insert(i,quat.w,{'NEEDED','FAST'}).interpolation = fcurve_interpolation # psa_bone.fcurve_quat_x.keyframe_points.insert(i,quat.x,{'NEEDED','FAST'}).interpolation = fcurve_interpolation # psa_bone.fcurve_quat_y.keyframe_points.insert(i,quat.y,{'NEEDED','FAST'}).interpolation = fcurve_interpolation # psa_bone.fcurve_quat_z.keyframe_points.insert(i,quat.z,{'NEEDED','FAST'}).interpolation = fcurve_interpolation # psa_bone.fcurve_loc_x.keyframe_points.insert(i,loc.x,{'NEEDED','FAST'}).interpolation = fcurve_interpolation # psa_bone.fcurve_loc_y.keyframe_points.insert(i,loc.y,{'NEEDED','FAST'}).interpolation = fcurve_interpolation # psa_bone.fcurve_loc_z.keyframe_points.insert(i,loc.z,{'NEEDED','FAST'}).interpolation = fcurve_interpolation raw_key_index += 1 # on first frame # break raw_key_index += ( NumRawFrames - min ( maxframes , NumRawFrames ) ) * Totalbones # Add action to tail of the nla track if bActionsToTrack : if nla_track_last_frame == 0 : nla_stripes . new ( Name , 0 , action ) else : nla_stripes . new ( Name , nla_stripes [ - 1 ] . frame_end , action ) nla_track_last_frame += NumRawFrames elif is_first_action : first_action = action is_first_action = False print ( "Done: %f sec." % ( time . process_time ( ) - ref_time ) ) # break on first animation set # break scene = util_get_scene ( context ) if not bActionsToTrack : if not scene . is_nla_tweakmode : armature_obj . animation_data . action = first_action if bUpdateTimelineRange : scene . frame_start = 0 if bActionsToTrack : scene . frame_end = sum ( frames for _ , _ , _ , frames in Action_List ) - 1 else : scene . frame_end = max ( frames for _ , _ , _ , frames in Action_List ) - 1 util_select_all ( False ) util_obj_select ( context , armature_obj ) util_obj_set_active ( context , armature_obj ) # 2.8 crashes if not is_blen_280 : scene . frame_set ( 0 )