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
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))
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)
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)
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)
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
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()
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