[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