[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