[Bf-extensions-cvs] [850e731] fbx_io_development: FBX import: add skinning.

Bastien Montagne noreply at git.blender.org
Sun May 4 12:08:45 CEST 2014


Commit: 850e731a1e3b336d6978a731a3efaf075dd945b8
Author: Bastien Montagne
Date:   Sun May 4 12:02:44 2014 +0200
https://developer.blender.org/rBA850e731a1e3b336d6978a731a3efaf075dd945b8

FBX import: add skinning.

Now limbnode/clusters are converted to valid armature bones and vgroups,
and mesh objetcs get parented/bound to armatures.

Note this is rather complex to handle, mostly due to spaces mess...

Only tested with relatively simple testcases, still need real-life validation.

===================================================================

M	io_scene_fbx/import_fbx.py

===================================================================

diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index 808e112..33978ee 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -350,8 +350,8 @@ def blen_read_object(fbx_tmpl, fbx_obj, object_data):
 # --------
 # Armature
 
-def blen_read_armatures_add_bone(bl_obj, bl_arm, bones, b_uuid):
-    from mathutils import Matrix, Euler, Vector
+def blen_read_armatures_add_bone(bl_obj, bl_arm, bones, b_uuid, matrices):
+    from mathutils import Matrix, Vector
 
     b_item, bsize, p_uuid, clusters = bones[b_uuid]
     fbx_bdata, bl_bdata = b_item
@@ -361,21 +361,29 @@ def blen_read_armatures_add_bone(bl_obj, bl_arm, bones, b_uuid):
     p_ebo = None
     if p_uuid is not None:
         # Recurse over parents!
-        blen_read_armatures_add_bone(bl_obj, bl_arm, bones, p_uuid)
+        blen_read_armatures_add_bone(bl_obj, bl_arm, bones, p_uuid, matrices)
         p_ebo = bones[p_uuid][0][1]
 
     # Remember we assume (for now) that one bone only has one cluster, this will have to be checked ultimately.
-    fbx_cdata = clusters[0][0]
+    if not clusters:
+        return None, None
+    fbx_cdata, meshes, objects = clusters[0]
+    objects = {o[1] for o in objects}
 
     # We assume matrices in cluster are rest pose of bones (they are in Global space!).
     # TransformLink is matrix of bone, in global space.
     # TransformAssociateModel is matrix of armature, in global space (at bind time).
     # We seek for matrix of bone in armature space...
+    elm = elem_find_first(fbx_cdata, b'Transform', default=None)
+    mmat_bone = array_to_matrix4(elm.props[0]) if elm is not None else None
     elm = elem_find_first(fbx_cdata, b'TransformLink', default=None)
     bmat_glob = array_to_matrix4(elm.props[0]) if elm is not None else Matrix()
     elm = elem_find_first(fbx_cdata, b'TransformAssociateModel', default=None)
-    omat_glob = array_to_matrix4(elm.props[0]) if elm is not None else Matrix()
-    bmat_arm = omat_glob.inverted() * bmat_glob
+    amat_glob = array_to_matrix4(elm.props[0]) if elm is not None else Matrix()
+
+    # ----
+    # Now, create the (edit)bone.
+    bmat_arm = amat_glob.inverted() * bmat_glob
     bone_name_utf8 = elem_name_ensure_class(fbx_bdata, b'Model')
 
     b_item[1] = ebo = bl_arm.edit_bones.new(name=bone_name_utf8)
@@ -391,10 +399,45 @@ def blen_read_armatures_add_bone(bl_obj, bl_arm, bones, b_uuid):
         if similar_vectors(p_ebo.tail, ebo.head):
             ebo.use_connect = True
 
+    # ----
+    # Add a new vgroup to the meshes (their objects, actually!).
+    # Quite obviously, only one mesh is expected...
+    elm = elem_find_first(fbx_cdata, b'Indexes', default=None)
+    indices = elm.props[0] if elm is not None else ()
+    elm = elem_find_first(fbx_cdata, b'Weights', default=None)
+    weights = elm.props[0] if elm is not None else ()
+    if indices and len(indices) == len(weights):
+        for obj in objects:
+            # We replace/override here...
+            if bone_name_utf8 not in obj.vertex_groups:
+                vg = obj.vertex_groups.new(bone_name_utf8)
+            else:
+                vg = obj.vertex_groups[bone_name_utf8]
+            for i, w in zip(indices, weights):
+                vg.add((i,), w, 'REPLACE')
+
+    # ----
+    # If we get a valid mesh matrix (in bone space), store armature and mesh global matrices, we need to set temporarily
+    # both objects to those matrices when actually binding them via the modifier.
+    # Note we assume all bones were bound with the same mesh/armature (global) matrix, we do not support otherwise
+    # in Blender anyway!
+    if mmat_bone is not None:
+        mmat_glob = bmat_glob * mmat_bone
+        for obj in objects:
+            if obj in matrices:
+                continue
+            matrices[obj] = (amat_glob, mmat_glob)
+
+
+def blen_read_armatures(fbx_tmpl, armatures, scene, global_matrix):
+    from mathutils import Matrix
+
+    if global_matrix is None:
+        global_matrix = Matrix()
 
-def blen_read_armatures(fbx_tmpl, armatures, scene):
     for a_item, bones in armatures:
         fbx_adata, bl_adata = a_item
+        matrices = {}
 
         # ----
         # Armature data.
@@ -414,10 +457,28 @@ def blen_read_armatures(fbx_tmpl, armatures, scene):
         bpy.ops.object.mode_set(mode='EDIT')
 
         for b_uuid in bones:
-            blen_read_armatures_add_bone(bl_adata, bl_arm, bones, b_uuid)
+            blen_read_armatures_add_bone(bl_adata, bl_arm, bones, b_uuid, matrices)
 
         bpy.ops.object.mode_set(mode='OBJECT')
 
+        # Bind armature to objects.
+        arm_mat_back = bl_adata.matrix_basis.copy()
+        for ob_me, (amat, mmat) in matrices.items():
+            # bring global armature & mesh matrices into *Blender* global space.
+            amat = global_matrix * amat
+            mmat = global_matrix * mmat
+
+            bl_adata.matrix_basis = amat
+            me_mat_back = ob_me.matrix_basis.copy()
+            ob_me.matrix_basis = mmat
+
+            mod = ob_me.modifiers.new(elem_name_utf8, 'ARMATURE')
+            mod.object = bl_adata
+
+            ob_me.parent = bl_adata
+            ob_me.matrix_basis = me_mat_back
+        bl_adata.matrix_basis = arm_mat_back
+
 
 # ----
 # Mesh
@@ -1223,6 +1284,7 @@ def load(operator, context, filepath="",
                         if fbx_cdata is None or fbx_cdata.id != b'Deformer' or fbx_cdata.props[2] != b'Cluster':
                             continue
                         meshes = set()
+                        objects = []
                         for s_uuid, s_ctype in fbx_connection_map.get(c_uuid, ()):
                             if s_ctype.props[0] != b'OO':
                                 continue
@@ -1237,9 +1299,20 @@ def load(operator, context, filepath="",
                                     continue
                                 # Blenmeshes are assumed already created at that time!
                                 assert(isinstance(bl_mdata, bpy.types.Mesh))
+                                # And we have to find all objects using this mesh!
+                                for o_uuid, o_ctype in fbx_connection_map.get(m_uuid, ()):
+                                    if o_ctype.props[0] != b'OO':
+                                        continue
+                                    fbx_odata, bl_odata = o_item = fbx_table_nodes.get(o_uuid, (None, None))
+                                    if fbx_odata is None or fbx_odata.id != b'Model' or fbx_odata.props[2] != b'Mesh':
+                                        continue
+                                    # bl_odata is still None, objects have not yet been created...
+                                    objects.append(o_item)
                                 meshes.add(bl_mdata)
                             # Skin deformers are only here to connect clusters to meshes, for us, nothing else to do.
-                        clusters.append((fbx_cdata, meshes))
+                        print(objects)
+                        print(meshes)
+                        clusters.append((fbx_cdata, meshes, objects))
                     # For now, we assume there is only one cluster & skin per bone (at least for a given armature)!
                     assert(len(clusters) <= 1)
                     bones[b_uuid] = (b_item, size, p_uuid if p_uuid != a_uuid else None, clusters)
@@ -1302,7 +1375,7 @@ def load(operator, context, filepath="",
     def _():
         fbx_tmpl = fbx_template_get((b'Model', b'KFbxNode'))
 
-        blen_read_armatures(fbx_tmpl, armatures, scene)
+        blen_read_armatures(fbx_tmpl, armatures, scene, global_matrix)
     _(); del _
 
     def _():



More information about the Bf-extensions-cvs mailing list