[Bf-extensions-cvs] [2bc5fbf] master: FBX: add support for baked animated armature export.
Bastien Montagne
noreply at git.blender.org
Mon Apr 7 17:47:53 CEST 2014
Commit: 2bc5fbf3ea41e9816555d3e8b9fc7bb02297be5e
Author: Bastien Montagne
Date: Mon Apr 7 17:44:52 2014 +0200
https://developer.blender.org/rBA2bc5fbf3ea41e9816555d3e8b9fc7bb02297be5e
FBX: add support for baked animated armature export.
Note: tested on unity & houdini with some basic mesh/rig, needs some more serious testing with real animated characters!
===================================================================
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 60325fd..b301244 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -34,7 +34,7 @@ from itertools import zip_longest, chain
import bpy
import bpy_extras
-from bpy.types import Object, Bone
+from bpy.types import Object, Bone, PoseBone
from mathutils import Vector, Matrix
from . import encode_bin, data_types
@@ -957,6 +957,8 @@ def fbx_template_def_animcurve(scene, settings, override_defaults=None, nbr_user
##### FBX objects generators. #####
def has_valid_parent(scene_data, obj):
+ if isinstance(obj, PoseBone):
+ obj = obj.bone
return obj.parent and obj.parent in scene_data.objects
@@ -964,7 +966,7 @@ def use_bake_space_transform(scene_data, obj):
# NOTE: Only applies to object types supporting this!!! Currently, only meshes...
# Also, do not apply it to children objects.
# TODO: Check whether this can work for bones too...
- return (scene_data.settings.bake_space_transform and not isinstance(obj, Bone) and
+ return (scene_data.settings.bake_space_transform and not isinstance(obj, (PoseBone, Bone)) and
obj.type in {'MESH'} and not has_valid_parent(scene_data, obj))
@@ -979,43 +981,49 @@ def fbx_object_matrix(scene_data, obj, armature=None, local_space=False, global_
If obj is a bone, and global_space is True, armature must be provided (it's the bone's armature object!).
Applies specific rotation to bones, lamps and cameras (conversion Blender -> FBX).
"""
- is_bone = isinstance(obj, Bone)
+ is_posebone = isinstance(obj, PoseBone)
+ is_bone = is_posebone or isinstance(obj, Bone)
# Objects which are not bones and do not have any parent are *always* in global space (unless local_space is True!).
is_global = not local_space and (global_space or not (is_bone or has_valid_parent(scene_data, obj)))
- #assert((is_bone and is_global and armature is None) == False,
- #"You must provide an armature object to get bones transform matrix in global space!")
-
- matrix = obj.matrix_local
-
- # Lamps, cameras and bones need to be rotated (in local space!).
- if is_bone:
- matrix = matrix * MAT_CONVERT_BONE
- elif obj.type == 'LAMP':
- matrix = matrix * MAT_CONVERT_LAMP
- elif obj.type == 'CAMERA':
- matrix = matrix * MAT_CONVERT_CAMERA
-
# Up till here, our matrix is in local space, time to bring it in its final desired space.
if is_bone:
+ bo = obj
+ matrix = (bo.matrix if is_posebone else bo.matrix_local) * MAT_CONVERT_BONE
+
# Bones are in armature (object) space currently, either bring them to global space or real
# local space (relative to parent bone).
if is_global:
matrix = armature.matrix_world * matrix
- elif obj.parent: # Parent bone, get matrix relative to it.
- par_matrix = obj.parent.matrix_local * MAT_CONVERT_BONE
- matrix = par_matrix.inverted() * matrix
- elif obj.parent:
- if is_global:
- # Move matrix to global Blender space.
- matrix = obj.parent.matrix_world * matrix
- elif use_bake_space_transform(scene_data, obj.parent):
- # Blender's and FBX's local space of parent may differ if we use bake_space_transform...
- # Apply parent's *Blender* local space...
- matrix = obj.parent.matrix_local * matrix
- # ...and move it back into parent's *FBX* local space.
- par_mat = fbx_object_matrix(scene_data, obj.parent, local_space=True)
- matrix = par_mat.inverted() * matrix
+ else: # Handle parent bone is needed.
+ par_matrix = None
+ if is_posebone and bo.bone.parent:
+ par_matrix = scene_data.bones_to_posebones[bo.bone.parent].matrix
+ elif bo.parent:
+ par_matrix = bo.parent.matrix_local
+ if par_matrix:
+ par_matrix = par_matrix * MAT_CONVERT_BONE
+ matrix = par_matrix.inverted() * matrix
+ else:
+ matrix = obj.matrix_local
+
+ # Lamps, and cameras need to be rotated (in local space!).
+ if obj.type == 'LAMP':
+ matrix = matrix * MAT_CONVERT_LAMP
+ elif obj.type == 'CAMERA':
+ matrix = matrix * MAT_CONVERT_CAMERA
+
+ if obj.parent:
+ if is_global:
+ # Move matrix to global Blender space.
+ matrix = obj.parent.matrix_world * matrix
+ elif use_bake_space_transform(scene_data, obj.parent):
+ # Blender's and FBX's local space of parent may differ if we use bake_space_transform...
+ # Apply parent's *Blender* local space...
+ matrix = obj.parent.matrix_local * matrix
+ # ...and move it back into parent's *FBX* local space.
+ 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):
# If we bake the transforms we need to post-multiply inverse global transform.
@@ -1984,7 +1992,7 @@ FBXData = namedtuple("FBXData", (
"templates", "templates_users", "connections",
"settings", "scene", "objects", "animations",
"data_empties", "data_lamps", "data_cameras", "data_meshes", "mesh_mat_indices",
- "data_bones", "data_deformers",
+ "bones_to_posebones", "data_bones", "data_deformers",
"data_world", "data_materials", "data_textures", "data_videos",
))
@@ -2033,7 +2041,8 @@ def fbx_mat_properties_from_texture(tex):
return tex_fbx_props
-def fbx_skeleton_from_armature(scene, settings, armature, objects, data_bones, data_deformers, arm_parents):
+def fbx_skeleton_from_armature(scene, settings, armature, objects, bones_to_posebones,
+ data_bones, data_deformers, 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).
@@ -2042,9 +2051,10 @@ def fbx_skeleton_from_armature(scene, settings, armature, objects, data_bones, d
"""
arm = armature.data
bones = OrderedDict()
- for bo in arm.bones:
+ for bo, pbo in zip(arm.bones, armature.pose.bones):
key, data_key = get_blender_bone_key(armature, bo)
objects[bo] = key
+ bones_to_posebones[bo] = pbo
data_bones[bo] = (key, data_key, armature)
bones[bo.name] = bo
@@ -2131,6 +2141,7 @@ def fbx_animations_objects(scene_data):
objects = scene_data.objects
bake_step = scene_data.settings.bake_anim_step
scene = scene_data.scene
+ bone_map = scene_data.bones_to_posebones
# FBX mapping info: Property affected, and name of the "sub" property (to distinguish e.g. vector's channels).
fbx_names = (
@@ -2146,10 +2157,10 @@ def fbx_animations_objects(scene_data):
while currframe < scene.frame_end:
scene.frame_set(int(currframe), currframe - int(currframe))
for obj in objects.keys():
- if isinstance(obj, Bone):
- continue # TODO!
+ # Get PoseBone from bone...
+ tobj = bone_map[obj] if isinstance(obj, Bone) else obj
# We compute baked loc/rot/scale for all objects.
- loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, obj)
+ loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, tobj)
tx = tuple(loc) + tuple(units_convert_iter(rot, "radian", "degree")) + tuple(scale)
animdata[obj].append((currframe, tx, [False] * len(tx)))
currframe += bake_step
@@ -2170,7 +2181,9 @@ def fbx_animations_objects(scene_data):
if wrt:
curves[idx].append((currframe, val))
- loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, obj)
+ # Get PoseBone from bone...
+ tobj = bone_map[obj] if isinstance(obj, Bone) else obj
+ loc, rot, scale, _m, _mr = fbx_object_tx(scene_data, tobj)
tx = tuple(loc) + tuple(units_convert_iter(rot, "radian", "degree")) + tuple(scale)
# If animation for a channel, (True, keyframes), else (False, current value).
final_keys = OrderedDict()
@@ -2217,11 +2230,13 @@ def fbx_data_from_scene(scene, settings):
# Armatures!
data_bones = OrderedDict()
data_deformers = OrderedDict()
+ bones_to_posebones = dict()
arm_parents = set()
for obj in tuple(objects.keys()):
if obj.type not in {'ARMATURE'}:
continue
- fbx_skeleton_from_armature(scene, settings, obj, objects, data_bones, data_deformers, arm_parents)
+ fbx_skeleton_from_armature(scene, settings, obj, objects, bones_to_posebones,
+ data_bones, data_deformers, arm_parents)
# Some world settings are embedded in FBX materials...
if scene.world:
@@ -2289,7 +2304,7 @@ def fbx_data_from_scene(scene, settings):
None, None, None,
settings, scene, objects, None,
data_empties, data_lamps, data_cameras, data_meshes, None,
- data_bones, data_deformers,
+ bones_to_posebones, data_bones, data_deformers,
data_world, data_materials, data_textures, data_videos,
)
animations = fbx_animations_objects(tmp_scdata)
@@ -2477,7 +2492,7 @@ def fbx_data_from_scene(scene, settings):
templates, templates_users, connections,
settings, scene, objects, animations,
data_empties, data_lamps, data_cameras, data_meshes, mesh_mat_indices,
- data_bones, data_deformers,
+ bones_to_posebones, data_bones, data_deformers,
data_world, data_materials, data_textures, data_videos,
)
More information about the Bf-extensions-cvs
mailing list