[Bf-extensions-cvs] [99dcf0d] fbx_io_development: FBX export: first attempt to support shape keys.

Bastien Montagne noreply at git.blender.org
Wed Jun 11 16:51:24 CEST 2014


Commit: 99dcf0d273e05333940a77c305fb7a3ab6e0ba55
Author: Bastien Montagne
Date:   Wed Jun 11 16:47:00 2014 +0200
https://developer.blender.org/rBA99dcf0d273e05333940a77c305fb7a3ab6e0ba55

FBX export: first attempt to support shape keys.

Note: though produced 'code' looks OK, it seems broken (crashes Unity, and also fails in converter).
Maybe we need the 'fake anim' (i.e. with stack, layer and curvenodes, but *without* curves, though
their uuid are listed in connections !!!!) generated by converter from a collada export?

I can’t say how much sick I am of this "format"... Each time I add something it turns into
a dirty blind fight in pitch black room.

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

M	io_scene_fbx/export_fbx_bin.py
M	io_scene_fbx/fbx_utils.py

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

diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index c902a66..dd2cbac 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -42,6 +42,7 @@ from .fbx_utils import (
     FBX_GEOMETRY_VERSION, FBX_GEOMETRY_NORMAL_VERSION, FBX_GEOMETRY_BINORMAL_VERSION, FBX_GEOMETRY_TANGENT_VERSION,
     FBX_GEOMETRY_SMOOTHING_VERSION, FBX_GEOMETRY_VCOLOR_VERSION, FBX_GEOMETRY_UV_VERSION,
     FBX_GEOMETRY_MATERIAL_VERSION, FBX_GEOMETRY_LAYER_VERSION,
+    FBX_GEOMETRY_SHAPE_VERSION, FBX_DEFORMER_SHAPE_VERSION, FBX_DEFORMER_SHAPECHANNEL_VERSION,
     FBX_POSE_BIND_VERSION, FBX_DEFORMER_SKIN_VERSION, FBX_DEFORMER_CLUSTER_VERSION,
     FBX_MATERIAL_VERSION, FBX_TEXTURE_VERSION,
     FBX_ANIM_KEY_VERSION,
@@ -50,13 +51,14 @@ from .fbx_utils import (
     FBX_LIGHT_TYPES, FBX_LIGHT_DECAY_TYPES,
     RIGHT_HAND_AXES, FBX_FRAMERATES,
     # Miscellaneous utils.
-    units_convert, units_convert_iter, matrix_to_array, similar_values,
+    units_convert, units_convert_iter, matrix_to_array, similar_values, similar_values_iter,
     # UUID from key.
     get_fbx_uuid_from_key,
     # Key generators.
     get_blenderID_key, get_blenderID_name,
+    get_blender_mesh_shape_key, get_blender_mesh_shape_channel_key,
     get_blender_empty_key, get_blender_bone_key,
-    get_blender_armature_bindpose_key, get_blender_armature_skin_key, get_blender_bone_cluster_key,
+    get_blender_bindpose_key, get_blender_armature_skin_key, get_blender_bone_cluster_key,
     get_blender_anim_id_base, get_blender_anim_stack_key, get_blender_anim_layer_key,
     get_blender_anim_curve_node_key, get_blender_anim_curve_key,
     # FBX element data.
@@ -655,6 +657,102 @@ def fbx_data_camera_elements(root, cam_obj, scene_data):
     elem_data_single_float64(cam, b"CameraOrthoZoom", 1.0)
 
 
+def fbx_data_bindpose_element(root, me_obj, me, scene_data, arm_obj=None, bones=[]):
+    """
+    Helper, since bindpose are used by both meshes shape keys and armature bones...
+    """
+    if arm_obj is None:
+        arm_obj = me_obj
+    # We assume bind pose for our bones are their "Editmode" pose...
+    # All matrices are expected in global (world) space.
+    bindpose_key = get_blender_bindpose_key(arm_obj.bdata, me)
+    fbx_pose = elem_data_single_int64(root, b"Pose", get_fbx_uuid_from_key(bindpose_key))
+    fbx_pose.add_string(fbx_name_class(me.name.encode(), b"Pose"))
+    fbx_pose.add_string(b"BindPose")
+
+    elem_data_single_string(fbx_pose, b"Type", b"BindPose")
+    elem_data_single_int32(fbx_pose, b"Version", FBX_POSE_BIND_VERSION)
+    elem_data_single_int32(fbx_pose, b"NbPoseNodes", 1 + len(bones))
+
+    # First node is mesh/object.
+    mat_world_obj = me_obj.fbx_object_matrix(scene_data, global_space=True)
+    fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
+    elem_data_single_int64(fbx_posenode, b"Node", me_obj.fbx_uuid)
+    elem_data_single_float64_array(fbx_posenode, b"Matrix", matrix_to_array(mat_world_obj))
+    # And all bones of armature!
+    mat_world_bones = {}
+    for bo_obj in bones:
+        bomat = bo_obj.fbx_object_matrix(scene_data, rest=True, global_space=True)
+        mat_world_bones[bo_obj] = bomat
+        fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
+        elem_data_single_int64(fbx_posenode, b"Node", bo_obj.fbx_uuid)
+        elem_data_single_float64_array(fbx_posenode, b"Matrix", matrix_to_array(bomat))
+
+    return mat_world_obj, mat_world_bones
+
+
+def fbx_data_mesh_shapes_elements(root, me_obj, me, scene_data, fbx_me_tmpl, fbx_me_props):
+    """
+    Write shape keys related data.
+    """
+    if me not in scene_data.data_deformers_shape:
+        return
+
+    # First, write the geometry data itself (i.e. shapes).
+    _me_key, shape_key, shapes = scene_data.data_deformers_shape[me]
+
+    channels = []
+
+    for shape, (channel_key, geom_key) in shapes.items():
+        # Only write vertices really different from org coordinates!
+        shape_verts_co = []
+        shape_verts_idx = []
+        for idx, (sv, v) in enumerate(zip(shape.data, me.vertices)):
+            if similar_values_iter(sv.co, v.co):
+                continue
+            shape_verts_co.extend(sv.co);
+            shape_verts_idx.append(idx);
+        shape_verts_weights = [100.0] * (len(shape_verts_co) // 3)  # for now, we can use vgroup later here!
+        channels.append((channel_key, shape, shape_verts_weights))
+
+        geom = elem_data_single_int64(root, b"Geometry", get_fbx_uuid_from_key(geom_key))
+        geom.add_string(fbx_name_class(shape.name.encode(), b"Geometry"))
+        geom.add_string(b"Shape")
+
+        tmpl = elem_props_template_init(scene_data.templates, b"Geometry")
+        props = elem_properties(geom)
+        elem_props_template_finalize(tmpl, props)
+
+        elem_data_single_int32(geom, b"Version", FBX_GEOMETRY_SHAPE_VERSION)
+
+        elem_data_single_int32_array(geom, b"Indexes", shape_verts_idx)
+        elem_data_single_float64_array(geom, b"Vertices", shape_verts_co)
+        elem_data_single_float64_array(geom, b"Normals", [0.0] * len(shape_verts_co))
+
+    # Yiha! BindPose for shapekeys too! Dodecasigh...
+    # XXX Not sure yet whether several bindposes on same mesh are allowed, or not... :/
+    fbx_data_bindpose_element(root, me_obj, me, scene_data)
+
+    # ...and now, the deformers stuff.
+    fbx_shape = elem_data_single_int64(root, b"Deformer", get_fbx_uuid_from_key(shape_key))
+    fbx_shape.add_string(fbx_name_class(me.name.encode(), b"Deformer"))
+    fbx_shape.add_string(b"BlendShape")
+
+    elem_data_single_int32(fbx_shape, b"Version", FBX_DEFORMER_SHAPE_VERSION)
+
+    for channel_key, shape, shape_verts_weights in channels:
+        fbx_channel = elem_data_single_int64(root, b"Deformer", get_fbx_uuid_from_key(channel_key))
+        fbx_channel.add_string(fbx_name_class(shape.name.encode(), b"SubDeformer"))
+        fbx_channel.add_string(b"BlendShapeChannel")
+
+        elem_data_single_int32(fbx_channel, b"Version", FBX_DEFORMER_SHAPECHANNEL_VERSION)
+        elem_data_single_float64(fbx_channel, b"DeformPercent", shape.value * 100.0)  # Percents...
+        elem_data_single_float64_array(fbx_channel, b"FullWeights", shape_verts_weights)
+
+        # *WHY* add this in linked mesh properties too? *cry*
+        # No idea whether it’s percent here too, or more usual factor :/
+        elem_props_template_set(fbx_me_tmpl, fbx_me_props, "p_number", shape.name.encode(), shape.value, animatable=True)
+
 def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
     """
     Write the Mesh (Geometry) data block.
@@ -673,7 +771,8 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
     # No gscale/gmat here, all data are supposed to be in object space.
     smooth_type = scene_data.settings.mesh_smooth_type
 
-    do_bake_space_transform = ObjectWrapper(me_obj).use_bake_space_transform(scene_data)
+    me_obj = ObjectWrapper(me_obj)
+    do_bake_space_transform = me_obj.use_bake_space_transform(scene_data)
 
     # Vertices are in object space, but we are post-multiplying all transforms with the inverse of the
     # global matrix, so we need to apply the global matrix to the vertices to get the correct result.
@@ -692,8 +791,6 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
     tmpl = elem_props_template_init(scene_data.templates, b"Geometry")
     props = elem_properties(geom)
 
-    elem_props_template_finalize(tmpl, props)
-
     # Custom properties.
     if scene_data.settings.use_custom_properties:
         fbx_data_element_custom_properties(props, me)
@@ -1035,6 +1132,10 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
             elem_data_single_string(lay_tan, b"Type", b"LayerElementTangent")
             elem_data_single_int32(lay_tan, b"TypedIndex", tspaceidx)
 
+    # Shape keys...
+    fbx_data_mesh_shapes_elements(root, me_obj, me, scene_data, tmpl, props)
+
+    elem_props_template_finalize(tmpl, props)
     done_meshes.add(me_key)
 
 
@@ -1240,12 +1341,13 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
         if scene_data.settings.use_custom_properties:
             fbx_data_element_custom_properties(props, bo)
 
-    # Deformers and BindPoses.
+    # Skin deformers and BindPoses.
     # Note: we might also use Deformers for our "parent to vertex" stuff???
-    deformer = scene_data.data_deformers.get(arm_obj, None)
+    deformer = scene_data.data_deformers_skin.get(arm_obj, None)
     if deformer is not None:
         for me, (skin_key, ob_obj, clusters) in deformer.items():
             # BindPose.
+            """
             # We assume bind pose for our bones are their "Editmode" pose...
             # All matrices are expected in global (world) space.
             bindpose_key = get_blender_armature_bindpose_key(arm_obj.bdata, me)
@@ -1270,6 +1372,9 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
                 fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
                 elem_data_single_int64(fbx_posenode, b"Node", bo_obj.fbx_uuid)
                 elem_data_single_float64_array(fbx_posenode, b"Matrix", matrix_to_array(bomat))
+            """
+
+            mat_worl_obj, mat_world_bones = fbx_data_bindpose_element(root, ob_obj, me, scene_data, arm_obj, bones)
 
             # Deformer.
             fbx_skin = elem_data_single_int64(root, b"Deformer", get_fbx_uuid_from_key(skin_key))
@@ -1516,7 +1621,7 @@ def fbx_mat_properties_from_texture(tex):
 
 
 def fbx_skeleton_from_armature(scene, settings, arm_obj, objects, data_meshes,
-                               data_bones, data_deformers, arm_parents):
+                               data_bones, data_deformers_skin, arm_parents):
     """
     Create skeleton from armature/bones (NodeAttribute/LimbNode and Model/LimbNode), and for each deformed mesh,
     create Pose/BindPose(with sub PoseNode) and Deformer/Skin(with Deformer/SubDeformer/Cluster).
@@ -1568,8 +1673,8 @@ def fbx_skeleton_from_armature(scene, settings, arm_obj, objects, data_meshes,
         # Create skin & clusters relations (note skins are connected to geometry, *not* model!).
         _key, me, _free = data_mes

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list