def euler_angle_to_quaternions(x, y, z):
    transform = fbx.FbxAMatrix()
    transform.SetIdentity()
    transform.SetR(fbx.FbxVector4(
        x, y, z, 0.0))  # euler angle is 'eEulerXYZ' here, i.e. apply Z first
    q = transform.GetQ()
    return q
Beispiel #2
0
    def populateSkins(self, fbxNode):
        fbxNodeAttribute = fbxNode.GetNodeAttribute()
        if fbxNodeAttribute:
            fbxAttributeType = fbxNodeAttribute.GetAttributeType()
            if (fbx.FbxNodeAttribute.eMesh == fbxAttributeType
                    or fbx.FbxNodeAttribute.eSubDiv == fbxAttributeType):
                fbxMesh = fbxNode.GetNodeAttribute()
                for i in range(fbxMesh.GetDeformerCount(
                        fbx.FbxDeformer.eSkin)):
                    fbxSkin = fbxMesh.GetDeformer(i, fbx.FbxDeformer.eSkin)
                    # try to find skeleton root (.eSkeleton) in parent nodes
                    root = self.findSkelRoot(fbxSkin.GetCluster(0).GetLink(
                    )) if fbxSkin.GetClusterCount() > 0 else None
                    skin = usdUtils.Skin(root)
                    for clusterIdx in range(fbxSkin.GetClusterCount()):
                        fbxCluster = fbxSkin.GetCluster(clusterIdx)
                        fbxJointNode = fbxCluster.GetLink()
                        skin.joints.append(fbxJointNode)

                        linkWorldTransform = fbx.FbxAMatrix()
                        linkWorldTransform = fbxCluster.GetTransformLinkMatrix(
                            linkWorldTransform)
                        skin.bindMatrices[
                            fbxJointNode] = GfMatrix4dWithFbxMatrix(
                                linkWorldTransform)
                    self.skinning.skins.append(skin)
                    self.fbxSkinToSkin[fbxSkin] = skin

        for childIdx in xrange(fbxNode.GetChildCount()):
            self.populateSkins(fbxNode.GetChild(childIdx))
Beispiel #3
0
    def applySkinning(self, fbxNode, fbxSkin, usdMesh, indices):
        skin = self.fbxSkinToSkin[fbxSkin]
        skeleton = skin.skeleton

        maxPointIndex = 0
        for clusterIdx in range(fbxSkin.GetClusterCount()):
            fbxCluster = fbxSkin.GetCluster(clusterIdx)
            for i in range(fbxCluster.GetControlPointIndicesCount()):
                pointIndex = fbxCluster.GetControlPointIndices()[i]
                if maxPointIndex < pointIndex:
                    maxPointIndex = pointIndex
        vertexCount = maxPointIndex + 1  # should be equal to number of vertices: max(indices) + 1

        jointIndicesPacked = [[] for i in range(vertexCount)]
        weightsPacked = [[] for i in range(vertexCount)]
        for clusterIdx in range(fbxSkin.GetClusterCount()):
            fbxCluster = fbxSkin.GetCluster(clusterIdx)
            for i in range(fbxCluster.GetControlPointIndicesCount()):
                pointIndex = fbxCluster.GetControlPointIndices()[i]
                jointIndicesPacked[pointIndex].append(
                    skin.remapIndex(clusterIdx))
                weightsPacked[pointIndex].append(
                    float(fbxCluster.GetControlPointWeights()[i]))

        components = 0
        for indicesPerVertex in jointIndicesPacked:
            if components < len(indicesPerVertex):
                components = len(indicesPerVertex)

        jointIndices = [0] * vertexCount * components
        weights = [float(0)] * vertexCount * components
        for i in range(vertexCount):
            indicesPerVertex = jointIndicesPacked[i]
            for j in range(len(indicesPerVertex)):
                jointIndices[i * components + j] = indicesPerVertex[j]
                weights[i * components + j] = weightsPacked[i][j]
        weights = Vt.FloatArray(weights)
        UsdSkel.NormalizeWeights(weights, components)

        usdSkelBinding = UsdSkel.BindingAPI(usdMesh)
        usdSkelBinding.CreateJointIndicesPrimvar(False,
                                                 components).Set(jointIndices)
        usdSkelBinding.CreateJointWeightsPrimvar(False,
                                                 components).Set(weights)

        bindTransform = Gf.Matrix4d(1)
        if fbxSkin.GetClusterCount() > 0:
            # FBX stores bind transform matrix for the skin in each cluster
            # get it from the first one
            fbxCluster = fbxSkin.GetCluster(0)
            fbxBindTransform = fbx.FbxAMatrix()
            fbxBindTransform = fbxCluster.GetTransformMatrix(fbxBindTransform)
            bindTransform = GfMatrix4dWithFbxMatrix(fbxBindTransform)

        usdSkelBinding.CreateGeomBindTransformAttr(bindTransform)
        usdSkelBinding.CreateSkeletonRel().AddTarget(
            skeleton.usdSkeleton.GetPath())
        if self.legacyModifier is not None:
            self.legacyModifier.addSkelAnimToMesh(usdMesh, skeleton)
Beispiel #4
0
def getFbxNodeGeometricTransform(fbxNode):
    # geometry transform is an additional transform for geometry
    # it is relative to the node transform
    # this transform is not distributing to the children nodes in scene graph
    translation = fbxNode.GetGeometricTranslation(fbx.FbxNode.eSourcePivot)
    rotation = fbxNode.GetGeometricRotation(fbx.FbxNode.eSourcePivot)
    scale = fbxNode.GetGeometricScaling(fbx.FbxNode.eSourcePivot)
    return fbx.FbxAMatrix(translation, rotation, scale)
def get_node_dict(scene, root_name):
    pre_transform = fbx.FbxAMatrix()
    pre_transform.SetIdentity()
    pelvis, pre_transform = get_node(scene.GetRootNode(), root_name,
                                     pre_transform)
    nodes, parents, joint_names = get_nodes(pelvis)
    rs = dict()
    for i in range(len(joint_names)):
        rs[joint_names[i]] = nodes[i]
    return rs
def get_node(node, joint_name, transform):
    if node.GetName() == joint_name:
        return node, transform

    # copy matrix considering python passes pointer
    transform = fbx.FbxAMatrix(node.EvaluateLocalTransform() * transform)
    for i in range(node.GetChildCount()):
        parent = node.GetChild(i)
        found_node, transform = get_node(parent, joint_name, transform)
        if found_node:
            return found_node, transform
    return None, transform
def StoreBindPose(pSdkManager, pScene, pPatchNode):
    lClusteredFbxNodes = []
    if pPatchNode and pPatchNode.GetNodeAttribute():
        lSkinCount = 0
        lClusterCount = 0
        lNodeAttributeType = pPatchNode.GetNodeAttribute().GetAttributeType()
        if lNodeAttributeType in (fbx.FbxNodeAttribute.eMesh,
                                  fbx.FbxNodeAttribute.eNurbs,
                                  fbx.FbxNodeAttribute.ePatch):
            lSkinCount = pPatchNode.GetNodeAttribute().GetDeformerCount(
                fbx.FbxDeformer.eSkin)
            for i in range(lSkinCount):
                lSkin = pPatchNode.GetNodeAttribute().GetDeformer(
                    i, fbx.FbxDeformer.eSkin)
                lClusterCount += lSkin.GetClusterCount()

        # If we found some clusters we must add the node
        if lClusterCount:
            # Again, go through all the skins get each cluster link and add them
            for i in range(lSkinCount):
                lSkin = pPatchNode.GetNodeAttribute().GetDeformer(
                    i, fbx.FbxDeformer.eSkin)
                lClusterCount = lSkin.GetClusterCount()
                for j in range(lClusterCount):
                    lClusterNode = lSkin.GetCluster(j).GetLink()
                    AddNodeRecursively(lClusteredFbxNodes, lClusterNode)

            # Add the patch to the pose
            lClusteredFbxNodes += [pPatchNode]

    # Now create a bind pose with the link list
    if len(lClusteredFbxNodes):
        # A pose must be named. Arbitrarily use the name of the patch node.
        lPose = fbx.FbxPose.Create(pSdkManager, pPatchNode.GetName())
        lPose.SetIsBindPose(True)

        for lFbxNode in lClusteredFbxNodes:
            lBindMatrix = fbx.FbxAMatrix()
            lScene = lFbxNode.GetScene()
            if lScene:
                lBindMatrix = lScene.GetAnimationEvaluator(
                ).GetNodeGlobalTransform(lFbxNode)
            lPose.Add(lFbxNode, fbx.FbxMatrix(lBindMatrix))

        # Add the pose to the scene
        pScene.AddPose(lPose)
def LinkMeshToSkeleton(pSdkManager, pPatchNode, pSkeletonRoot):
    lLimbNode1 = pSkeletonRoot.GetChild(0)

    # Bottom section of cylinder is clustered to skeleton root.
    lClusterToRoot = fbx.FbxCluster.Create(pSdkManager, "")
    lClusterToRoot.SetLink(pSkeletonRoot)
    lClusterToRoot.SetLinkMode(fbx.FbxCluster.eTotalOne)
    for i in range(3):
        lClusterToRoot.AddControlPointIndex(i, 1)

    # Center section of cylinder is clustered to skeleton limb node.
    lClusterToLimbNode1 = fbx.FbxCluster.Create(pSdkManager, "")
    lClusterToLimbNode1.SetLink(lLimbNode1)
    lClusterToLimbNode1.SetLinkMode(fbx.FbxCluster.eTotalOne)

    # Now we have the Patch and the skeleton correctly positioned,
    # set the Transform and TransformLink matrix accordingly.
    lXMatrix = fbx.FbxAMatrix()
    lScene = pPatchNode.GetScene()
    if lScene:
        lXMatrix = lScene.GetAnimationEvaluator().GetNodeGlobalTransform(
            pPatchNode)
    lClusterToRoot.SetTransformMatrix(lXMatrix)
    lClusterToLimbNode1.SetTransformMatrix(lXMatrix)
    lScene = pSkeletonRoot.GetScene()
    if lScene:
        lXMatrix = lScene.GetAnimationEvaluator().GetNodeGlobalTransform(
            pSkeletonRoot)
    lClusterToRoot.SetTransformLinkMatrix(lXMatrix)
    lScene = lLimbNode1.GetScene()
    if lScene:
        lXMatrix = lScene.GetAnimationEvaluator().GetNodeGlobalTransform(
            lLimbNode1)
    lClusterToLimbNode1.SetTransformLinkMatrix(lXMatrix)

    # Add the clusters to the patch by creating a skin and adding those clusters to that skin.
    # After add that skin.
    lSkin = fbx.FbxSkin.Create(pSdkManager, "")
    lSkin.AddCluster(lClusterToRoot)
    lSkin.AddCluster(lClusterToLimbNode1)
    pPatchNode.GetNodeAttribute().AddDeformer(lSkin)
Beispiel #9
0
def get_geometry_transform(node):
    gt = node.GetGeometricTranslation(fbx.FbxNode.eSourcePivot)
    gr = node.GetGeometricRotation(fbx.FbxNode.eSourcePivot)
    gs = node.GetGeometricScaling(fbx.FbxNode.eSourcePivot)
    return fbx.FbxAMatrix(gt, gr, gs)
Beispiel #10
0
def create_scene():
    fbx_manager = fbx.FbxManager()
    fbx_scene = fbx.FbxScene.Create(fbx_manager, "prototype_session_scene")
    root_node = fbx_scene.GetRootNode()

    # create some nodes
    loc_1 = fbxUtils.create_locator("loc1", fbx_manager, root_node)
    loc_2 = fbxUtils.create_locator("loc2", fbx_manager, root_node)
    loc_3 = fbxUtils.create_locator("loc3", fbx_manager, root_node)

    # maniuplate locators
    mat_1 = fbx.FbxAMatrix()
    mat_1.SetT(
        fbx.FbxVector4(1, 2, 3, 0)
    )

    mat_1.SetR(
        fbx.FbxVector4(45, 0, 0, 0)
    )

    # inspect matrix values
    if False:
        print mat_1.GetRow(0)
        print mat_1.GetRow(1)
        print mat_1.GetRow(2)
        print mat_1.GetRow(3)

    # test scale
    if False:
        mat_1.SetS(
            fbx.FbxVector4(2, 2, 2, 0)
        )

        print mat_1.GetRow(0)
        print mat_1.GetRow(1)
        print mat_1.GetRow(2)
        print mat_1.GetRow(3)

    # set node values
    loc_1.LclTranslation.Set(
        fbx.FbxDouble4(*list(mat_1.GetT()))
    )

    loc_1.LclRotation.Set(
        fbx.FbxDouble4(*list(mat_1.GetR()))
    )

    # test "aim"
    if False:
        # the aim matrix method aint gonna work
        # cos we can only set rows using FbxMatrix
        # but we can't get Euler values from it
        # and we can't turn it into FbxAMatrix
        # so I guess we have to do it the hard way?
        mat_2 = fbx.FbxMatrix()

        mat_2.SetRow(0, fbx.FbxVector4(1, 0, 0, 0))
        mat_2.SetRow(1, fbx.FbxVector4(0, 1, 0, 0))
        mat_2.SetRow(2, fbx.FbxVector4(0, 0, 1, 0))
        mat_2.SetRow(3, fbx.FbxVector4(2, 0, 0, 1))

        mat_2a = mat_2 * fbx.FbxAMatrix()

        loc_2.LclTranslation.Set(
            fbx.FbxDouble4(*list(mat_2a.GetT()))
        )

        loc_2.LclRotation.Set(
            fbx.FbxDouble4(*list(mat_2a.GetR()))
        )

    # in theory this method should support rotation orders...?

    # euler aim (x - aim, y - up)
    x_aim_vector = fbx.FbxVector4(0.5, 0.1, 0.4, 0)
    y_up_vector = fbx.FbxVector4(-0.2, 1, 0.1, 0)

    # calculate vectors
    x_aim_vector.Normalize()
    y_up_vector.Normalize()
    z_norm_vector = x_aim_vector.CrossProduct(y_up_vector)
#     z_norm_vector = y_up_vector.CrossProduct(x_aim_vector)
    z_norm_vector.Normalize()
#     y_corrected_up_vector = x_aim_vector.CrossProduct(z_norm_vector)
    y_corrected_up_vector = z_norm_vector.CrossProduct(x_aim_vector)

    if False:
        # debugging
        print "aim ", x_aim_vector, x_aim_vector.Length()
        print "up ", y_up_vector, y_up_vector.Length()
        print "cor up ", y_corrected_up_vector, y_corrected_up_vector.Length()
        print "norm ", z_norm_vector, z_norm_vector.Length()

        print "np x", numpy.cross(
            [0, 1, 0],
            [0, 0, 1],
        )

        print "np y", numpy.cross(
            [0, 0, 1],
            [1, 0, 0],
        )

        print "np z", numpy.cross(
            [1, 0, 0],
            [0, 1, 0],
        )

    if False:
        # create scipy rotation object
        # using direction cosine matrix

        directions = [
            [1, 0, 0, 0],
            [0, 1, 0, 0],
            [0, 0, 1, 0]
        ]

        x_aim_cosines = [
            fbx.FbxVector4(*direction).DotProduct(
                x_aim_vector,
            ) for direction in directions
        ]

        y_up_cosines = [
            fbx.FbxVector4(*direction).DotProduct(
                y_up_vector,
            ) for direction in directions
        ]

        z_norm_cosines = [
            fbx.FbxVector4(*direction).DotProduct(
                z_norm_vector,
            ) for direction in directions
        ]

        print numpy.array([
            x_aim_cosines,
            y_up_cosines,
            z_norm_cosines
        ])

        print numpy.rad2deg([
            x_aim_cosines,
            y_up_cosines,
            z_norm_cosines
        ])

    sp_rot = scipy_rotation.from_dcm(
        #         # [0.0, 0.0, -17.46279830041399]
        #         numpy.rad2deg([
        #             x_aim_cosines,
        #             y_up_cosines,
        #             z_norm_cosines
        #         ])
        #
        #         #[0.0, 0.0, -13.101645932991731]
        numpy.array([
            list(x_aim_vector)[:3],
            list(y_up_vector)[:3],
            list(z_norm_vector)[:3]
        ]).transpose()
        # [0.0, 0.0, -13.101645932991731]
        #         numpy.array([
        #             x_aim_cosines,
        #             y_up_cosines,
        #             z_norm_cosines
        #         ])
    )

    poop_aim = sp_rot.as_euler('ZYX', degrees=True).tolist()
    euler_aim = sp_rot.as_euler('XYZ', degrees=True).tolist()

    # test
    loc_2.LclTranslation.Set(
        fbx.FbxDouble4(2, 0, 0, 0)
    )

    loc_2.LclRotation.Set(
        fbx.FbxDouble4(*euler_aim + [0])
    )

    # test rotation order

    loc_3.SetRotationOrder(
        loc_3.eSourcePivot,
        fbx.eEulerZYX
    )

    loc_3.LclTranslation.Set(
        fbx.FbxDouble4(3, 0, 0, 0)
    )

    loc_3.LclRotation.Set(
        fbx.FbxDouble4(
            *poop_aim + [0]
            #*sp_rot.as_euler('zyx', degrees=True).tolist() + [0]
        )
    )

    loc_1_mat = loc_1.EvaluateGlobalTransform()
    print loc_1_mat.GetR()

    loc_2_mat = loc_2.EvaluateGlobalTransform()
    print loc_2_mat.GetR()

    loc_3_mat = loc_3.EvaluateGlobalTransform()
    print loc_3_mat.GetR()

    return fbx_manager, fbx_scene
Beispiel #11
0
def thingA1():
    """
    Create locator, give it some random local translate and rotate values.
    Get matrix via xform.
    Calculate matrix manually.
    Compare matrices.
    xform second locator with manual matrix.
    Compare locators.
    """

    loc = cmds.spaceLocator()[0]

    rotate = get_random_rotate(ROTATE_SEED)
    print "rotation: ", rotate

    rads = [math.radians(i) for i in rotate]
    print "radians: ", rads

    cmds.xform(loc, rotation=rotate)

    xform_matrix = cmds.xform(loc, query=True, matrix=True)
    print "maya xform: ", xform_matrix

    local_matrix = cmds.getAttr(loc + ".matrix")
    print "maya matrix attr: ", local_matrix

    x_rot, y_rot, z_rot = rads

    x_matrix = [
        [1.0, 0.0, 0.0, 0.0],
        [0.0, math.cos(x_rot), math.sin(x_rot), 0.0],
        [0.0, -math.sin(x_rot), math.cos(x_rot), 0.0],
        [0.0, 0.0, 0.0, 1.0]
    ]

    y_matrix = [
        [math.cos(y_rot), 0.0, -math.sin(y_rot), 0.0],
        [0.0, 1.0, 0.0, 0.0],
        [math.sin(y_rot), 0.0, math.cos(y_rot), 0.0],
        [0.0, 0.0, 0.0, 1.0]
    ]

    z_matrix = [
        [math.cos(z_rot), math.sin(z_rot), 0.0, 0.0],
        [-math.sin(z_rot), math.cos(z_rot), 0.0, 0.0],
        [0.0, 0.0, 1.0, 0.0],
        [0.0, 0.0, 0.0, 1.0]
    ]

    fbx_x_matrix = fbx.FbxMatrix()
    for i, row in enumerate(x_matrix):
        fbx_x_matrix.SetRow(i, fbx.FbxVector4(*row))

    fbx_y_matrix = fbx.FbxMatrix()
    for i, row in enumerate(y_matrix):
        fbx_y_matrix.SetRow(i, fbx.FbxVector4(*row))

    fbx_z_matrix = fbx.FbxMatrix()
    for i, row in enumerate(z_matrix):
        fbx_z_matrix.SetRow(i, fbx.FbxVector4(*row))

    transform = fbx_z_matrix * fbx_y_matrix * fbx_x_matrix

    # print matrix
    fbx_matrix = [j for i in range(4) for j in list(transform.GetRow(i))]
    print "fbx matrix: ", fbx_matrix

    # test on new loc
    loc2 = cmds.spaceLocator()[0]
    cmds.xform(loc2, matrix=fbx_matrix)

    # interesting to note that maya does not decompose
    # identical local rotate values to the original
    # loc2 and loc3 results are the same
    # axes align with loc1, but local values are different!
    loc3 = cmds.spaceLocator()[0]
    cmds.xform(loc3, matrix=local_matrix)

    # decompose via FbxMatrix, FbxQuaternion and FbxAMatrix
    # note this only works for XYZ rotation order
    pTranslation = fbx.FbxVector4()
    pRotation = fbx.FbxQuaternion()
    pShearing = fbx.FbxVector4()
    pScaling = fbx.FbxVector4()

    transform.GetElements(
        pTranslation,
        pRotation,
        pShearing,
        pScaling,
    )

    # test results
    # local values are same as maya's decompose!
    # as in correct axis alignment with loc1
    # but different local values to loc1
    test = fbx.FbxAMatrix()
    test.SetQ(pRotation)
    print "FbxAMatrix rotation: ", test.GetR()
Beispiel #12
0
def thingB1():
    """
    Create locator, give it some random local rotate values.
    Get matrix via xform.
    Decompose matrix to local rotate values.
    Compare values
    """

    # initialize a random rotation
    rotate = get_random_rotate(ROTATE_SEED)

    # create a locator and apply rotation
    loc = cmds.spaceLocator()[0]
    cmds.xform(loc, rotation=rotate)

    # query local matrix
    local_matrix = cmds.getAttr(loc + ".matrix")

    # seperate rows
    row_0 = local_matrix[0:4]
    row_1 = local_matrix[4:8]
    row_2 = local_matrix[8:12]
    row_3 = local_matrix[12:16]

    # apply matrix to new loc to compare how maya xform command performs
#     loc_1 = cmds.spaceLocator()[0]
    loc_1 = cmds.createNode("transform")

    cmds.xform(loc_1, matrix=local_matrix)
    maya_decomp = cmds.xform(loc_1, query=True, rotation=True)

    # construct fbx matrix
    fbx_matrix = fbx.FbxMatrix()
    for i, row in enumerate([row_0, row_1, row_2, row_3]):
        fbx_matrix.SetRow(i, fbx.FbxVector4(*row))

    # decompose via FbxMatrix, FbxQuaternion and FbxAMatrix
    # note this only works for XYZ rotation order
    pTranslation = fbx.FbxVector4()
    pRotation = fbx.FbxQuaternion()
    pShearing = fbx.FbxVector4()
    pScaling = fbx.FbxVector4()

    fbx_matrix.GetElements(
        pTranslation,
        pRotation,
        pShearing,
        pScaling,
    )

    test = fbx.FbxAMatrix()
    test.SetQ(pRotation)

    # manual decomp

    # nope try again (wrong order? xyz instead of zyx?)
    #rot_y = math.asin(row_2[0])

    # xyz order gives correct y
    # interesting to note sometimes this matches maya decomp
    # and sometimes is matches original rotation
    rot_y = -math.asin(row_0[2])
    rot_y_degs = math.degrees(rot_y)

    # not exactly
    # correct value but positive/negative often incorrect
    # not sure why
#     rot_x = math.acos(row_2[2] / math.cos(rot_y))
#     rot_x_degs = math.degrees(rot_x)
#     rot_x = math.asin(row_1[2] / math.cos(rot_y))
#     rot_x_degs = math.degrees(rot_x)
#
#     rot_z = math.acos(row_0[0] / math.cos(rot_y))
#     rot_z_degs = math.degrees(rot_z)

    # use sine instead of cosine to get pos/neg values
    # sometimes right!!
    # obviously there's certain conditions where this works
    # and others where it doesn't
    # probably some gimbal lock related stuff

    # TODO rot y seems to be correct 100% of the time
    # do some random tests to see if that's consistent

    # _p for partial
#     rot_x_p = math.asin(row_2[2] / math.sin(math.radians(90 - rot_y_degs)))
#     rot_x_degs = (math.degrees(rot_x_p) - 90) * -1
#
#     rot_z = math.asin(row_0[1] / math.sin(math.radians(90 - rot_y_degs)))
#     rot_z_degs = math.degrees(rot_z)

    # check for pos/neg
    # we can determine the direction of rotation by comparing
    # cosine and sine values of the angle
    # these can be found from particular rows and columns
    # of the matrix when written out algebraically
    # this seems to work consistently!!!

    sin_z = row_0[1] / math.cos(rot_y)
    cos_z = row_0[0] / math.cos(rot_y)
    print "cos z: {}, sin z: {}".format(cos_z, sin_z)

    if cos_z == 1:
        rot_z = 0.0
    elif cos_z == 0:
        rot_z = math.radians(180)
    elif sin_z == 1:
        rot_z = math.radians(90)
    elif sin_z == -1:
        rot_z = math.radians(-90)
    elif cos_z > 0 and sin_z > 0:
        print "+z"
        rot_z = math.acos(cos_z)
    elif cos_z < 0 and sin_z > 0:
        print "180-z"
        rot_z = math.radians(180) - math.asin(sin_z)
    elif cos_z > 0 and sin_z < 0:
        print "-z"
        rot_z = -math.acos(cos_z)
    elif cos_z < 0 and sin_z < 0:
        print "-180-z"
        rot_z = math.radians(-180) - math.asin(sin_z)
    elif cos_z > 1 or cos_z < -1 or sin_z > 1 or sin_z < -1:
        raise Exception("-1 > cos/sin > 1 out of  bounds")
        # TOOD if neccesary

    rot_z_degs = math.degrees(rot_z)

    # check for pos/neg
    cos_x = row_2[2] / math.cos(rot_y)

    sin_x = row_1[2] / math.cos(rot_y)

    print "cos x: {}, sin x: {}".format(cos_x, sin_x)

    if cos_x == 1:
        rot_x = 0.0
    elif cos_x == 0:
        rot_x = math.radians(180)
    elif sin_x == 1:
        rot_x = math.radians(90)
    elif sin_x == -1:
        rot_x = math.radians(-90)
    elif cos_x > 0 and sin_x > 0:
        print "+z"
        rot_x = math.acos(cos_x)
    elif cos_x < 0 and sin_x > 0:
        print "180-z"
        rot_x = math.radians(180) - math.asin(sin_x)
    elif cos_x > 0 and sin_x < 0:
        print "-z"
        rot_x = -math.acos(cos_x)
    elif cos_x < 0 and sin_x < 0:
        print "-180-z"
        rot_x = math.radians(-180) - math.asin(sin_x)
    elif cos_x > 1 or cos_x < -1 or sin_x > 1 or sin_x < -1:
        raise Exception("-1 > cos/sin > 1 out of  bounds")
        # TOOD if neccesary

    rot_x_degs = math.degrees(rot_x)

    # test going back through each rotation
    # Nope, can't work cos y is not the last rotation!
#     mat_y = fbx.FbxMatrix()
#     mat_y.SetRow(0, fbx.FbxVector4(math.cos(rot_y), 0, -math.sin(rot_y), 0))
#     mat_y.SetRow(1, fbx.FbxVector4(0, 1, 0, 0))
#     mat_y.SetRow(2, fbx.FbxVector4(math.sin(rot_y), 0, math.cos(rot_y), 0))
#     mat_y.SetRow(3, fbx.FbxVector4(0, 0, 0, 1))
#
#     mat_xz = mat_y.Inverse() * fbx_matrix
#
#     rot_x = math.acos(mat_xz.GetRow(1)[1])
#     rot_x_degs = math.degrees(rot_x)
#
#     rot_z_degs = 0  # TODO

    # test manual decomp
    loc_2 = cmds.spaceLocator()[0]
    cmds.xform(loc_2, rotation=(rot_x_degs, rot_y_degs, rot_z_degs))

    # test scipy direction cosines method

    # get direction cosines using dot product
    # this seems to suffer similar problems
    # to when we don't check for pos/neg rotations
    # sometimes is correct, others not
    # probably best to avoid using this
    # especially considering this module
    # is not present in the sourced maya version of scipy

    world_x_vec = fbx.FbxVector4(1, 0, 0)
    world_y_vec = fbx.FbxVector4(0, 1, 0)
    world_z_vec = fbx.FbxVector4(0, 0, 1)
    world_vectors = [world_x_vec, world_y_vec, world_z_vec]

    row_0_vec = fbx.FbxVector4(*row_0)
    row_1_vec = fbx.FbxVector4(*row_1)
    row_2_vec = fbx.FbxVector4(*row_2)

    row_vectors = [fbx.FbxVector4(*i) for i in row_0, row_1, row_2]

    # actually, we can determine if these should be positive or negative
    # from cross products etc.
    # would that actually help though??
    # TODO!

    direction_cosines = [
        [row_vec.DotProduct(w_vec) for w_vec in world_vectors]
        for row_vec in row_vectors
    ]

    sp_rot = scipy_rotation.from_dcm(
        numpy.array(direction_cosines)
    )

    sp_decomp = sp_rot.as_euler('XYZ', degrees=True)

#     loc_3 = cmds.spaceLocator()[0]
#     cmds.xform(loc_3, rotation=sp_decomp.tolist())

    # compare matrices
    print "maya matrix attr: ", local_matrix

    fbx_matrix_list = [j for i in range(4) for j in list(fbx_matrix.GetRow(i))]
    print "fbx matrix: ", fbx_matrix_list

    # print rows
    print "maya matrix rows:\n\t{}\n\t{}\n\t{}\n\t{}".format(
        row_0, row_1, row_2, row_3
    )

    # compare results
    print "rotation: ", rotate
    print "maya decomp: ", maya_decomp
    print "Fbx decomp: {}".format(list(test.GetR()))
    print "Scipy decomp: {}".format(sp_decomp.tolist())
    print "manual decomp: ", rot_x_degs, rot_y_degs, rot_z_degs
def import_fbx(filename, pelvis_name=None):
    """
    :param filename: fbx file name
    :param pelvis_name: the name of skeleton root
    :return:
        joint_names:    (J, )
        parents:        (J, )
        offsets:        (J, 3)
        qs:             (F, J, 4) quaternions
        translations:   (F, J, 3)
        pre_transform:  fbx.FbxAMatrix, the global transformation applied to the whole skeleton, e.g. rotX = 180
    """
    # Initialize scene
    manager = fbx.FbxManager.Create()
    ios = fbx.FbxIOSettings.Create(manager, fbx.IOSROOT)
    manager.SetIOSettings(ios)
    importer = fbx.FbxImporter.Create(manager, "")
    status = importer.Initialize(filename, -1, manager.GetIOSettings())
    if not status:
        status = importer.GetStatus()
        print("Call to FbxImporter::Initialize() failed")
        print("Error returned: %s" % status.GetErrorString())
        return

    scene = fbx.FbxScene.Create(manager, "scene")
    importer.Import(scene)
    importer.Destroy()

    # Load Skeleton
    pre_transform = fbx.FbxAMatrix()
    pre_transform.SetIdentity()
    if pelvis_name is not None:
        pelvis, pre_transform = get_node(scene.GetRootNode(), pelvis_name,
                                         pre_transform)
    else:
        root = scene.GetRootNode()
        pelvis = root
        for i in range(root.GetChildCount()):
            node = root.GetChild(i)
            if isinstance(node.GetNodeAttribute(), fbx.FbxSkeleton):
                pelvis = node
                break
    nodes, parents, joint_names = get_nodes(pelvis)

    # Load anim
    # nobjects = scene.GetSrcObjectCount()
    stack = scene.GetCurrentAnimationStack()
    layer = stack.GetMember(0)
    offsets, qs, ts = get_anim(layer, nodes)
    offsets[0] = np.zeros(3)
    qs_global, ts_global = get_global_transform(layer, nodes)

    # consider pre transform
    preQ = pre_transform.GetQ()  # fbx.FbxQuaternion
    qs[:, 0] = preQ * qs[:, 0]
    preT = fbxVec4ToList(pre_transform.GetT())  # (3, )
    ts[:, 0] += preT

    # fbx.FbxQuaternion to np array
    qs = fbxQuaternionToArray(qs)
    qs_global = fbxQuaternionToArray(qs_global)

    return joint_names, parents, offsets, qs, ts, qs_global, ts_global