[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