[Bf-extensions-cvs] [a6f8095] master: FBX: support exporting baked animation.

Bastien Montagne noreply at git.blender.org
Thu Mar 27 17:54:28 CET 2014


Commit: a6f8095463722bbdbff06de4db7b0fdd042bbbdc
Author: Bastien Montagne
Date:   Tue Mar 25 17:50:30 2014 +0100
https://developer.blender.org/rBAa6f8095463722bbdbff06de4db7b0fdd042bbbdc

FBX: support exporting baked animation.

Note this code is highly theorical, I could not get to test it really (other apps I have access to also fail at importing FBX anim generated from collada by FBXConverter :( ).

For now, only basic (loc/rot/scale) of objects is implemented. Will wait for other testers' feedback before going any further.

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

M	io_scene_fbx/export_fbx_bin.py

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

diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index 1a990f6..b957c74 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -62,6 +62,7 @@ FBX_DEFORMER_SKIN_VERSION = 101
 FBX_DEFORMER_CLUSTER_VERSION = 100
 FBX_MATERIAL_VERSION = 102
 FBX_TEXTURE_VERSION = 202
+FBX_ANIM_KEY_VERSION = 4008
 
 FBX_NAME_CLASS_SEP = b"\x00\x01"
 
@@ -241,6 +242,26 @@ def get_blender_bone_cluster_key(armature, mesh, bone):
                      get_blenderID_key(bone), "SubDeformerCluster"))
 
 
+def get_blender_anim_stack_key(scene):
+    """Return single anim stack key."""
+    return "|".join((get_blenderID_key(scene), "AnimStack"))
+
+
+def get_blender_anim_layer_key(ID):
+    """Return ID's anim layer key."""
+    return "|".join((get_blenderID_key(ID), "AnimLayer"))
+
+
+def get_blender_anim_curve_node_key(ID, fbx_prop_name):
+    """Return (ID, fbxprop) curve node key."""
+    return "|".join((get_blenderID_key(ID), fbx_prop_name, "AnimCurveNode"))
+
+
+def get_blender_anim_curve_key(ID, fbx_prop_name, fbx_prop_item_name):
+    """Return (ID, fbxprop, item) curve key."""
+    return "|".join((get_blenderID_key(ID), fbx_prop_name, fbx_prop_item_name, "AnimCurve"))
+
+
 ##### Element generators. #####
 
 # Note: elem may be None, in this case the element is not added to any parent.
@@ -341,6 +362,7 @@ def elem_data_vec_float64(elem, name, value):
 FBX_PROPERTIES_DEFINITIONS = {
     "p_bool": (b"bool", b"", "add_int32"),  # Yes, int32 for a bool (and they do have a core bool type)!!!
     "p_integer": (b"int", b"Integer", "add_int32"),
+    "p_ulonglong": (b"ULongLong", b"", "add_int64"),
     "p_enum": (b"enum", b"", "add_int32"),
     "p_number": (b"double", b"Number", "add_float64"),
     "p_visibility": (b"Visibility", b"", "add_float64"),
@@ -691,6 +713,52 @@ def fbx_template_def_deformer(scene, settings, override_defaults=None, nbr_users
     return FBXTemplate(b"Deformer", b"", props, nbr_users)
 
 
+def fbx_template_def_animstack(scene, settings, override_defaults=None, nbr_users=0):
+    props = OrderedDict((
+        (b"Description", ("", "p_string", False)),
+        (b"LocalStart", (0, "p_timestamp", False)),
+        (b"LocalStop", (0, "p_timestamp", False)),
+        (b"ReferenceStart", (0, "p_timestamp", False)),
+        (b"ReferenceStop", (0, "p_timestamp", False)),
+    ))
+    if override_defaults is not None:
+        props.update(override_defaults)
+    return FBXTemplate(b"AnimationStack", b"FbxAnimStack", props, nbr_users)
+
+
+def fbx_template_def_animlayer(scene, settings, override_defaults=None, nbr_users=0):
+    props = OrderedDict((
+        (b"Weight", (100.0, "p_number", True)),
+        (b"Mute", (False, "p_bool", False)),
+        (b"Solo", (False, "p_bool", False)),
+        (b"Lock", (False, "p_bool", False)),
+        (b"Color", ((0.8, 0.8, 0.8), "p_color_rgb", True)),
+        (b"BlendMode", (0, "p_enum", False)),
+        (b"RotationAccumulationMode", (0, "p_enum", False)),
+        (b"ScaleAccumulationMode", (0, "p_enum", False)),
+        (b"BlendModeBypass", (0, "p_ulonglong", False)),
+    ))
+    if override_defaults is not None:
+        props.update(override_defaults)
+    return FBXTemplate(b"AnimationLayer", b"FbxAnimLayer", props, nbr_users)
+
+
+def fbx_template_def_animcurvenode(scene, settings, override_defaults=None, nbr_users=0):
+    props = OrderedDict((
+        (b"d", (None, "p_compound", False)),
+    ))
+    if override_defaults is not None:
+        props.update(override_defaults)
+    return FBXTemplate(b"AnimationCurveNode", b"FbxAnimCurveNode", props, nbr_users)
+
+
+def fbx_template_def_animcurve(scene, settings, override_defaults=None, nbr_users=0):
+    props = OrderedDict()
+    if override_defaults is not None:
+        props.update(override_defaults)
+    return FBXTemplate(b"AnimationCurve", b"", props, nbr_users)
+
+
 ##### FBX objects generators. #####
 def has_valid_parent(scene_data, obj):
     return obj.parent and obj.parent in scene_data.objects
@@ -704,7 +772,7 @@ def use_bake_space_transform(scene_data, obj):
             obj.type in {'MESH'} and not has_valid_parent(scene_data, obj))
 
 
-def object_matrix(scene_data, obj, armature=None, local_space=False, global_space=False):
+def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_space=False):
     """
     Generate object transform matrix (*always* in matching *FBX* space!).
     If local_space is True, returned matrix is *always* in local space.
@@ -750,7 +818,7 @@ def object_matrix(scene_data, obj, armature=None, local_space=False, global_spac
             # Apply parent's *Blender* local space...
             matrix = obj.parent.matrix_local * matrix
             # ...and move it back into parent's *FBX* local space.
-            par_mat = object_matrix(scene_data, obj.parent, local_space=True)
+            par_mat = fbx_object_matrix(scene_data, obj.parent, local_space=True)
             matrix = par_mat.inverted() * matrix
 
     if use_bake_space_transform(scene_data, obj):
@@ -764,11 +832,11 @@ def object_matrix(scene_data, obj, armature=None, local_space=False, global_spac
     return matrix
 
 
-def object_tx(scene_data, obj):
+def fbx_object_tx(scene_data, obj):
     """
     Generate object transform data (always in local space when possible).
     """
-    matrix = object_matrix(scene_data, obj)
+    matrix = fbx_object_matrix(scene_data, obj)
     loc, rot, scale = matrix.decompose()
     matrix_rot = rot.to_matrix()
     rot = rot.to_euler()  # quat -> euler, we always use 'XYZ' order.
@@ -848,7 +916,7 @@ def fbx_data_camera_elements(root, cam_obj, scene_data):
 
     # Real data now, good old camera!
     # Object transform info.
-    loc, rot, scale, matrix, matrix_rot = object_tx(scene_data, cam_obj)
+    loc, rot, scale, matrix, matrix_rot = fbx_object_tx(scene_data, cam_obj)
     up = matrix_rot * Vector((0.0, 1.0, 0.0))
     to = matrix_rot * Vector((0.0, 0.0, -1.0))
     # Render settings.
@@ -1479,14 +1547,14 @@ def fbx_data_armature_elements(root, armature, scene_data):
             elem_data_single_int32(fbx_pose, b"NbPoseNodes", 1 + len(armature.data.bones))
 
             # First node is mesh/object.
-            mat_world_obj = object_matrix(scene_data, obj, global_space=True)
+            mat_world_obj = fbx_object_matrix(scene_data, obj, global_space=True)
             fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
             elem_data_single_int64(fbx_posenode, b"Node", get_fbxuid_from_key(scene_data.objects[obj]))
             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 in armature.data.bones:
-                bomat = object_matrix(scene_data, bo, armature, global_space=True)
+                bomat = fbx_object_matrix(scene_data, bo, armature, global_space=True)
                 mat_world_bones[bo] = bomat
                 fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
                 elem_data_single_int64(fbx_posenode, b"Node", get_fbxuid_from_key(scene_data.objects[bo]))
@@ -1557,7 +1625,7 @@ def fbx_data_object_elements(root, obj, scene_data):
     elem_data_single_int32(model, b"Version", FBX_MODELS_VERSION)
 
     # Object transform info.
-    loc, rot, scale, matrix, matrix_rot = object_tx(scene_data, obj)
+    loc, rot, scale, matrix, matrix_rot = fbx_object_tx(scene_data, obj)
     rot = tuple(units_convert_iter(rot, "radian", "degree"))
 
     tmpl = scene_data.templates[b"Model"]
@@ -1594,6 +1662,71 @@ def fbx_data_object_elements(root, obj, scene_data):
         elem_props_template_set(tmpl, props, "p_bool", b"ForegroundTransparent", True)
 
 
+def fbx_data_animation_elements(root, scene_data):
+    """
+    Write animation data.
+    """
+    animations = scene_data.animations
+    if not animations:
+        return
+    scene = scene_data.scene
+
+    fps = scene.render.fps / scene.render.fps_base
+    def keys_to_ktimes(keys):
+        return (int(v) for v in units_convert_iter((f / fps for f, _v in keys), "second", "ktime"))
+
+    astack_key, alayers = animations
+    acn_tmpl = scene_data.templates[b"AnimationCurveNode"]
+
+    # Animation stack.
+    astack = elem_data_single_int64(root, b"AnimationStack", get_fbxuid_from_key(astack_key))
+    astack.add_string(fbx_name_class(scene.name.encode(), b"AnimStack"))
+    astack.add_string(b"")
+
+    for obj, (alayer_key, acurvenodes) in alayers.items():
+        # Animation layer.
+        alayer = elem_data_single_int64(root, b"AnimationLayer", get_fbxuid_from_key(alayer_key))
+        alayer.add_string(fbx_name_class(obj.name.encode(), b"AnimLayer"))
+        alayer.add_string(b"")
+
+        for fbx_prop, (acurvenode_key, acurves) in acurvenodes.items():
+            # Animation curve node.
+            acurvenode = elem_data_single_int64(root, b"AnimationCurveNode", get_fbxuid_from_key(acurvenode_key))
+            acurvenode.add_string(fbx_name_class(fbx_prop.encode(), b"AnimCurveNode"))
+            acurvenode.add_string(b"")
+
+            acn_props = elem_properties(acurvenode)
+
+            for fbx_item, (acurve_key, default_value, keys) in acurves.items():
+                elem_props_template_set(acn_tmpl, acn_props, "p_number", fbx_item.encode(), default_value)
+
+                # Only create Animation curve if needed!
+                if keys:
+                    acurve = elem_data_single_int64(root, b"AnimationCurve", get_fbxuid_from_key(acurve_key))
+                    acurve.add_string(fbx_name_class(b"", b"AnimCurve"))
+                    acurve.add_string(b"")
+
+                    # key attributes...
+                    # flags...
+                    keyattr_flags = (1 << 3 |   # interpolation mode, 1 = constant, 2 = linear, 3 = cubic.
+                                     1 << 8 |   # tangent mode, 8 = auto, 9 = TCB, 10 = user, 11 = generic break,
+                                     1 << 13 |  # tangent mode, 12 = generic clamp, 13 = generic time independent,
+                                     1 << 14 |  # tangent mode, 13 + 14 = generic clamp progressive.
+                 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list