[Bf-extensions-cvs] [702f793] fbx_io_development: FBX import: add support for shapekeys.

Bastien Montagne noreply at git.blender.org
Tue Jun 24 16:45:31 CEST 2014


Commit: 702f793d0f9d0fa5cced3425ff1e628c10595fc6
Author: Bastien Montagne
Date:   Fri Jun 20 12:00:55 2014 +0200
https://developer.blender.org/rBA702f793d0f9d0fa5cced3425ff1e628c10595fc6

FBX import: add support for shapekeys.

This includes per-vertex weights (imported as vgroups).

No animation yet, though.

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

M	io_scene_fbx/import_fbx.py

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

diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index 8c4c1c2..142a4ff 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -133,8 +133,8 @@ def elem_uuid(elem):
     return elem.props[0]
 
 
-def elem_prop_first(elem):
-    return elem.props[0] if (elem is not None) and elem.props else None
+def elem_prop_first(elem, default=None):
+    return elem.props[0] if (elem is not None) and elem.props else default
 
 
 # ----
@@ -326,6 +326,21 @@ def blen_read_object_transform_do(transform_data):
     )
 
 
+# XXX This might be week, now that we can add vgroups from both bones and shapes, name collisions become
+#     more likely, will have to make this more robust!!!
+def add_vgroup_to_objects(vg_indices, vg_weights, vg_name, objects):
+    assert(len(vg_indices) == len(vg_weights))
+    if vg_indices:
+        for obj in objects:
+            # We replace/override here...
+            if vg_name not in obj.vertex_groups:
+                vg = obj.vertex_groups.new(vg_name)
+            else:
+                vg = obj.vertex_groups[vg_name]
+            for i, w in zip(vg_indices, vg_weights):
+                vg.add((i,), w, 'REPLACE')
+
+
 def blen_read_object_transform_preprocess(fbx_props, fbx_obj, rot_alt_mat):
     # This is quite involved, 'fbxRNode.cpp' from openscenegraph used as a reference
     const_vector_zero_3d = 0.0, 0.0, 0.0
@@ -425,7 +440,7 @@ def blen_read_armatures_add_bone(bl_obj, bl_arm, bones, b_uuid, matrices):
     if not clusters:
         return None
     fbx_cdata, meshes, objects = clusters[0]
-    objects = {o[1] for o in objects}
+    objects = {blen_o for fbx_o, blen_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.
@@ -461,19 +476,9 @@ def blen_read_armatures_add_bone(bl_obj, bl_arm, bones, b_uuid, matrices):
     # ----
     # 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 not in obj.vertex_groups:
-                vg = obj.vertex_groups.new(bone_name)
-            else:
-                vg = obj.vertex_groups[bone_name]
-            for i, w in zip(indices, weights):
-                vg.add((i,), w, 'REPLACE')
+    indices = elem_prop_first(elem_find_first(fbx_cdata, b'Indexes', default=None), default=())
+    weights = elem_prop_first(elem_find_first(fbx_cdata, b'Weights', default=None), default=())
+    add_vgroup_to_objects(indices, weights, bone_name, objects)
 
     # ----
     # If we get a valid mesh matrix (in bone space), store armature and mesh global matrices, we need to set temporarily
@@ -1097,6 +1102,40 @@ def blen_read_geom(fbx_tmpl, fbx_obj):
     return mesh
 
 
+def blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene, global_matrix):
+    from mathutils import Vector
+
+    elem_name_utf8 = elem_name_ensure_class(fbx_sdata, b'Geometry')
+    indices = elem_prop_first(elem_find_first(fbx_sdata, b'Indexes'), default=())
+    dvcos = tuple(co for co in zip(*[iter(elem_prop_first(elem_find_first(fbx_sdata, b'Vertices'), default=()))] * 3))
+    # We completely ignore normals here!
+    weight = elem_prop_first(elem_find_first(fbx_bcdata, b'DeformPercent'), default=100.0) / 100.0
+    vgweights = tuple(vgw / 100.0 for vgw in elem_prop_first(elem_find_first(fbx_bcdata, b'FullWeights'), default=()))
+
+    assert(len(vgweights) == len(indices) == len(dvcos))
+    create_vg = bool(set(vgweights) - {1.0})
+
+    for me, objects in meshes:
+        vcos = tuple((idx, me.vertices[idx].co + Vector(dvco)) for idx, dvco in zip(indices, dvcos))
+        objects = list({blen_o for fbx_o, blen_o in objects})
+        assert(objects)
+
+        if me.shape_keys is None:
+            objects[0].shape_key_add(name="Basis", from_mix=False)
+        objects[0].shape_key_add(name=elem_name_utf8, from_mix=False)
+        me.shape_keys.use_relative = True  # Should already be set as such.
+
+        kb = me.shape_keys.key_blocks[elem_name_utf8]
+        for idx, co in vcos:
+            kb.data[idx].co[:] = co
+        kb.value = weight
+
+        # Add vgroup if necessary.
+        if create_vg:
+            add_vgroup_to_objects(indices, vgweights, elem_name_utf8, objects)
+            kb.vertex_group = elem_name_utf8
+
+
 # --------
 # Material
 
@@ -1644,7 +1683,62 @@ def load(operator, context, filepath="",
                 obj_base.select = True
     _(); del _
 
-    # Now that we have objects, we can finish armatures processing.
+    # Now that we have objects...
+
+    # I) We can handle shapes.
+    shapes = {}
+    def _():
+        fbx_tmpl = fbx_template_get((b'Geometry', b'KFbxShape'))
+
+        for s_uuid, s_item in fbx_table_nodes.items():
+            fbx_sdata, bl_sdata = s_item = fbx_table_nodes.get(s_uuid, (None, None))
+            if fbx_sdata is None or fbx_sdata.id != b'Geometry' or fbx_sdata.props[2] != b'Shape':
+                continue
+
+            # shape -> blendshapechannel -> blendshape -> mesh.
+            blend_channels = []
+            for bc_uuid, bc_ctype in fbx_connection_map.get(s_uuid, ()):
+                if bc_ctype.props[0] != b'OO':
+                    continue
+                fbx_bcdata, _bl_bcdata = fbx_table_nodes.get(bc_uuid, (None, None))
+                if fbx_bcdata is None or fbx_bcdata.id != b'Deformer' or fbx_bcdata.props[2] != b'BlendShapeChannel':
+                    continue
+                meshes = []
+                objects = []
+                for bs_uuid, bs_ctype in fbx_connection_map.get(bc_uuid, ()):
+                    if bs_ctype.props[0] != b'OO':
+                        continue
+                    fbx_bsdata, _bl_bsdata = fbx_table_nodes.get(bs_uuid, (None, None))
+                    if fbx_bsdata is None or fbx_bsdata.id != b'Deformer' or fbx_bsdata.props[2] != b'BlendShape':
+                        continue
+                    for m_uuid, m_ctype in fbx_connection_map.get(bs_uuid, ()):
+                        if m_ctype.props[0] != b'OO':
+                            continue
+                        fbx_mdata, bl_mdata = fbx_table_nodes.get(m_uuid, (None, None))
+                        if fbx_mdata is None or fbx_mdata.id != b'Geometry' or fbx_mdata.props[2] != b'Mesh':
+                            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!
+                        objects = []
+                        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.append((bl_mdata, objects))
+                    # BlendShape deformers are only here to connect BlendShapeChannels to meshes, nothing else to do.
+                blend_channels.append((fbx_bcdata, meshes))
+
+                blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene, global_matrix)
+            shapes[s_uuid] = blend_channels
+    _(); del _
+    print(shapes)
+
+    # II) We can finish armatures processing.
     def _():
         fbx_tmpl = fbx_template_get((b'Model', b'KFbxNode'))



More information about the Bf-extensions-cvs mailing list