Announcements

Between mid-October and November, the content on AREA will be relocated to the Autodesk Community M&E Hub and the Autodesk Community Gallery. Learn more HERE.

How to rig a mesh of obj file with FBX Python SDK

How to rig a mesh of obj file with FBX Python SDK

Anonymous
Not applicable
2,722 Views
1 Reply
Message 1 of 2

How to rig a mesh of obj file with FBX Python SDK

Anonymous
Not applicable

What I want to do is import obj file and rig it with FBX Python SDK. As far as I understand from the sample code of ExportScene01.py, what I should do is as follow.

 

  1. create fbx manager and fbx scene
  2. load obj file and fill the scene with it
  3. create skeletons and their hierarchy, and add skeleton root to scene
  4. create clusters for each skeleton
  5. create skin (deformer) and add clusters to it
  6. add skin to mesh attribute
  7. save to fbx file

However, when I import fbx file to Autodesk Maya, the following error occurs.

 

 

The imported scene has no initial binding position (Bind Pose) for the skin. The plug-in will compute one automatically. However, running the 'Go To Bind Pose' command may create unexpected results

 

 

My code is based on ExportScene01.py but why do I need to initialize binding position in my case? and actually I don't know how to do it.

 

In addition, when I import the fbx file to blender, blender doesn't display mesh, and some skeletons' positions are incorrect.

 

I exported fbx files of "mesh only", "skeletons only", "mesh and skeletons (without binding)" each, and their result looks good. So maybe the problem occurs at binding. Could anybody help me, please? I'm struggling to deal with this problem for about a week.

 

My codes are as follow:

 

 

import FbxCommon
from fbx import *

def get_keypoint_pos_dict():
    keypoint_dict = {
        "Neck": [0.502, 0.722, 0.490],
        "RShoulder": [0.428, 0.721, 0.480],
        "RElbow": [0.354, 0.622, 0.482],
        "RWrist": [0.278, 0.540, 0.545],
        ...
        "RSmallToe": [0.397, 0.068, 0.520],
        "RHeel": [0.425, 0.090, 0.452],
    }
    return keypoint_dict_filtered

def create_skeleton_tree(fbx_manager, keypoint_dict):

    # root node
    root_name = "skeleton_root"
    root_attr = FbxSkeleton.Create(fbx_manager, root_name)
    root_attr.SetSkeletonType(FbxSkeleton.eRoot)
    root_node = FbxNode.Create(fbx_manager, root_name)
    root_node.SetNodeAttribute(root_attr)
    root_node.LclScaling.Set(FbxDouble3(0.1, 0.1, 0.1))

    # skeletons
    skeleton_nodes = {}
    for key in keypoint_dict:
        node_name = key
        node_attr = FbxSkeleton.Create(fbx_manager, node_name)
        node_attr.SetSkeletonType(FbxSkeleton.eLimbNode)
        node = FbxNode.Create(fbx_manager, "skeleton_" + node_name)
        node.SetNodeAttribute(node_attr)
        node.LclScaling.Set(FbxDouble3(0.1, 0.1, 0.1))
        skeleton_nodes[key] = node

    root_node.AddChild(skeleton_nodes["MidHip"])
    skeleton_nodes["MidHip"].AddChild(skeleton_nodes["Neck"])
    skeleton_nodes["Neck"].AddChild(skeleton_nodes["LShoulder"])
    skeleton_nodes["LShoulder"].AddChild(skeleton_nodes["LElbow"])
    skeleton_nodes["LElbow"].AddChild(skeleton_nodes["LWrist"])
    skeleton_nodes["Neck"].AddChild(skeleton_nodes["RShoulder"])
    skeleton_nodes["RShoulder"].AddChild(skeleton_nodes["RElbow"])
    skeleton_nodes["RElbow"].AddChild(skeleton_nodes["RWrist"])
    ...
    skeleton_nodes["RBigToe"].AddChild(skeleton_nodes["RSmallToe"])

    return root_node

def set_skeleton_pose(skeleton_node, keypoint_pos_dict):
    
    if skeleton_node.GetNodeAttribute():
        skeleton_type = skeleton_node.GetNodeAttribute().GetSkeletonType()

        if skeleton_type == FbxSkeleton.eRoot:
            skeleton_node.LclTranslation.Set(FbxDouble3(0.0, 0.0, 0.0))
        elif skeleton_type ==  FbxSkeleton.eLimbNode:
            skeleton_node_name = skeleton_node.GetName()
            parent_node_name = skeleton_node.GetParent().GetName()

            skeleton_node_pos = np.array(keypoint_pos_dict[skeleton_node_name.replace("skeleton_", "")])
            if parent_node_name == "skeleton_root":
                parent_node_pos = np.array([0.0, 0.0, 0.0])
            else:
                parent_node_pos = np.array(keypoint_pos_dict[parent_node_name.replace("skeleton_", "")])

            local_translation = skeleton_node_pos - parent_node_pos
            skeleton_node.LclTranslation.Set(FbxDouble3(*local_translation))
    
    for i in range(skeleton_node.GetChildCount()):
        set_skeleton_pose(skeleton_node.GetChild(i), keypoint_pos_dict)

def create_cps_weight_dict(mesh_attr, keypoint_pos_dict):
    cps_weight_dict = {
        "Neck": [0.0, 0.0, 0.0, ...],
        "RShoulder": [0.0, 0.0, 0.0, ...],
        "RElbow": [0.0, 0.0, 0.0, ...],
        "RWrist": [0.2, 0.2, 0.2, ...],
        ...
        "RSmallToe": [0.0, 0.0, 0.0, ...],
        "RHeel": [0.0, 0.0, 0.0, ...],
    }
   
    return cps_weight_dict

def create_skin(manager, scene, mesh_node, skeleton_root, cps_weight_dict):
    cluster_to_root = FbxCluster.Create(manager, "cluster_to_root")
    cluster_to_root.SetLink(skeleton_root)
    cluster_to_root.SetLinkMode(FbxCluster.eAdditive)

    cluster_dict = {}
    for key in cps_weight_dict:
        cluster_dict[key] = FbxCluster.Create(manager, "cluster_to_"+key)
        cluster_dict[key].SetLink(scene.FindNodeByName("skeleton_"+key))
        cluster_dict[key].SetLinkMode(FbxCluster.eAdditive)
        for i in range(len(cps_weight_dict[key])):
            if cps_weight_dict[key][i] != 0:
                cluster_dict[key].AddControlPointIndex(i, cps_weight_dict[key][i])

    # set transform matrix (matrix from mesh node)
    x_mat = scene.GetAnimationEvaluator().GetNodeGlobalTransform(mesh_node)
    cluster_to_root.SetTransformMatrix(x_mat)
    for key in cluster_dict:
        cluster_dict[key].SetTransformMatrix(x_mat)
    
    # set transform link matrix (root node)
    x_mat = scene.GetAnimationEvaluator().GetNodeGlobalTransform(skeleton_root)
    cluster_to_root.SetTransformLinkMatrix(x_mat)
    
    # set transform link matrix (each limb)
    for key in cluster_dict:
        x_mat = scene.GetAnimationEvaluator().GetNodeGlobalTransform(scene.FindNodeByName("skeleton_"+key))
        cluster_dict[key].SetTransformLinkMatrix(x_mat)
    
    # create skin and add cluster
    skin = FbxSkin.Create(manager, "")
    skin.AddCluster(cluster_to_root)
    for key in cluster_dict:
        skin.AddCluster(cluster_dict[key])
    
    return skin


if __name__ == "__main__":
    manager, scene = FbxCommon.InitializeSdkObjects()

    print("Add Mesh")
    FbxCommon.LoadScene(manager, scene, "./mesh_file.obj")
    scene.GetRootNode().GetChild(0).GetChild(0).SetName("mesh_node")
    FbxGeometryConverter(manager).Triangulate(scene, True)

    print("Add Skeleton")
    keypoint_pos_dict = get_keypoint_pos_dict()
    skeleton_root = create_skeleton_tree(manager, keypoint_pos_dict)
    set_skeleton_pose(skeleton_root, keypoint_pos_dict)
    scene.GetRootNode().GetChild(0).AddChild(skeleton_root)

    print("Calculate weight for each control points, for each keypoint")
    mesh_node = scene.FindNodeByName("mesh_node")
    mesh_node = scene.GetRootNode().GetChild(0).GetChild(0)
    mesh_attr = mesh_node.GetNodeAttribute()
    cps_weight_dict = create_cps_weight_dict(mesh_attr, keypoint_pos_dict)

    print("Create skin")
    skin = create_skin(manager, scene, mesh_node, skeleton_root, cps_weight_dict)

    print("Bind")
    mesh_attr.AddDeformer(skin)

    print("Save to fbx")
    FbxCommon.SaveScene(manager, scene, "./mesh_with_bind.fbx", pFileFormat=0)

 

0 Likes
2,723 Views
1 Reply
Reply (1)
Message 2 of 2

regalir
Autodesk
Autodesk

You don't need to define a Bind Pose in the FBX file but, in this case, you will get the the

The imported scene has no initial binding position ...

message (which is not an error), just a warning that want you to realize that, because there is not BindPose definition in the file, if you try to call the 'Go To Bind Pose' command of Maya, you will probably don't get the expected result.

 

The ExportScene01.py sample, does show you how to create the bind pose object. Since you have already computed the skeleton and the clusters, I believe that you just need a small modification to the existing StoreBindPose function to make it work with your setup.

 

I cannot answer about the Blender questions because  I don't know how they implemented their FBX import code.

0 Likes