Newer
Older
def get_matrix_rest_local(self):
if self._tag == 'BO':
# Bone.matrix_local is in armature space, bring in back in real local one!
par = self.bdata.parent
par_mat_inv = par.matrix_local.inverted_safe() if par else Matrix()
return par_mat_inv * self.bdata.matrix_local
else:
return self.matrix_local.copy()
matrix_rest_local = property(get_matrix_rest_local)
def get_matrix_rest_global(self):
if self._tag == 'BO':
return self._ref.matrix_world * self.bdata.matrix_local
else:
return self.matrix_global.copy()
matrix_rest_global = property(get_matrix_rest_global)
def has_valid_parent(self, objects):
par = self.parent
if par in objects:
if self._tag == 'OB':
par_type = self.bdata.parent_type
if par_type in {'OBJECT', 'BONE'}:
return True
else:
print("Sorry, “{}” parenting type is not supported".format(par_type))
return False
return True
return False
def use_bake_space_transform(self, scene_data):
# NOTE: Only applies to object types supporting this!!! Currently, only meshes and the like...
# TODO: Check whether this can work for bones too...
return (scene_data.settings.bake_space_transform and self._tag in {'OB', 'DP'} and
Bastien Montagne
committed
self.bdata.type in BLENDER_OBJECT_TYPES_MESHLIKE | {'EMPTY'})
def fbx_object_matrix(self, scene_data, rest=False, 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.
Else if global_space is True, returned matrix is always in world space.
If both local_space and global_space are False, returned matrix is in parent space if parent is valid,
else in world space.
Note local_space has precedence over global_space.
If rest is True and object is a Bone, returns matching rest pose transform instead of current pose one.
Applies specific rotation to bones, lamps and cameras (conversion Blender -> FBX).
"""
# 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 (self._tag in {'DP', 'BO'} or self.has_valid_parent(scene_data.objects))))
# Objects (meshes!) parented to armature are not parented to anything in FBX, hence we need them
# in global space, which is their 'virtual' local space...
is_global = is_global or self.parented_to_armature
# Since we have to apply corrections to some types of object, we always need local Blender space here...
matrix = self.matrix_rest_local if rest else self.matrix_local
parent = self.parent
# Bones, lamps and cameras need to be rotated (in local space!).
if self._tag == 'BO':
Bastien Montagne
committed
# If we have a bone parent we need to undo the parent correction.
if not is_global and scene_data.settings.bone_correction_matrix_inv and parent and parent.is_bone:
matrix = scene_data.settings.bone_correction_matrix_inv * matrix
# Apply the bone correction.
if scene_data.settings.bone_correction_matrix:
matrix = matrix * scene_data.settings.bone_correction_matrix
elif self.bdata.type == 'LAMP':
matrix = matrix * MAT_CONVERT_LAMP
elif self.bdata.type == 'CAMERA':
matrix = matrix * MAT_CONVERT_CAMERA
if self._tag in {'DP', 'OB'} and parent:
if parent._tag == 'BO':
# In bone parent case, we get transformation in **bone tip** space (sigh).
# Have to bring it back into bone root, which is FBX expected value.
matrix = Matrix.Translation((0, (parent.bdata.tail - parent.bdata.head).length, 0)) * matrix
# Our matrix is in local space, time to bring it in its final desired space.
if parent:
if is_global:
# Move matrix to global Blender space.
matrix = (parent.matrix_rest_global if rest else parent.matrix_global) * matrix
elif parent.use_bake_space_transform(scene_data):
# Blender's and FBX's local space of parent may differ if we use bake_space_transform...
# Apply parent's *Blender* local space...
matrix = (parent.matrix_rest_local if rest else parent.matrix_local) * matrix
# ...and move it back into parent's *FBX* local space.
par_mat = parent.fbx_object_matrix(scene_data, rest=rest, local_space=True)
matrix = par_mat.inverted_safe() * matrix
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
if self.use_bake_space_transform(scene_data):
# If we bake the transforms we need to post-multiply inverse global transform.
# This means that the global transform will not apply to children of this transform.
matrix = matrix * scene_data.settings.global_matrix_inv
if is_global:
# In any case, pre-multiply the global matrix to get it in FBX global space!
matrix = scene_data.settings.global_matrix * matrix
return matrix
def fbx_object_tx(self, scene_data, rest=False, rot_euler_compat=None):
"""
Generate object transform data (always in local space when possible).
"""
matrix = self.fbx_object_matrix(scene_data, rest=rest)
loc, rot, scale = matrix.decompose()
matrix_rot = rot.to_matrix()
# quat -> euler, we always use 'XYZ' order, use ref rotation if given.
if rot_euler_compat is not None:
rot = rot.to_euler('XYZ', rot_euler_compat)
else:
rot = rot.to_euler('XYZ')
return loc, rot, scale, matrix, matrix_rot
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
def get_is_object(self):
return self._tag == 'OB'
is_object = property(get_is_object)
def get_is_dupli(self):
return self._tag == 'DP'
is_dupli = property(get_is_dupli)
def get_is_bone(self):
return self._tag == 'BO'
is_bone = property(get_is_bone)
def get_type(self):
if self._tag in {'OB', 'DP'}:
return self.bdata.type
return ...
type = property(get_type)
def get_armature(self):
if self._tag == 'BO':
return ObjectWrapper(self._ref)
return None
armature = property(get_armature)
def get_bones(self):
if self._tag == 'OB' and self.bdata.type == 'ARMATURE':
return (ObjectWrapper(bo, self.bdata) for bo in self.bdata.data.bones)
return ()
bones = property(get_bones)
def get_material_slots(self):
if self._tag in {'OB', 'DP'}:
return self.bdata.material_slots
return ()
material_slots = property(get_material_slots)
Bastien Montagne
committed
def is_deformed_by_armature(self, arm_obj):
if not (self.is_object and self.type == 'MESH'):
return False
Bastien Montagne
committed
if self.parent == arm_obj and self.bdata.parent_type == 'ARMATURE':
Bastien Montagne
committed
return True
for mod in self.bdata.modifiers:
if mod.type == 'ARMATURE' and mod.object in {arm_obj.bdata, arm_obj.bdata.proxy}:
Bastien Montagne
committed
return True
def dupli_list_create(self, scene, settings='PREVIEW'):
if self._tag == 'OB' and self.bdata.is_duplicator:
self.bdata.dupli_list_create(scene, settings)
def dupli_list_clear(self):
if self._tag == 'OB'and self.bdata.is_duplicator:
self.bdata.dupli_list_clear()
def get_dupli_list(self):
if self._tag == 'OB'and self.bdata.is_duplicator:
return (ObjectWrapper(dup) for dup in self.bdata.dupli_list)
return ()
dupli_list = property(get_dupli_list)
def fbx_name_class(name, cls):
return FBX_NAME_CLASS_SEP.join((name, cls))
# ##### Top-level FBX data container. #####
# Helper sub-container gathering all exporter settings related to media (texture files).
Jens Ch. Restemeier
committed
FBXExportSettingsMedia = namedtuple("FBXExportSettingsMedia", (
"path_mode", "base_src", "base_dst", "subdir",
"embed_textures", "copy_set", "embedded_set",
))
# Helper container gathering all exporter settings.
Jens Ch. Restemeier
committed
FBXExportSettings = namedtuple("FBXExportSettings", (
"report", "to_axes", "global_matrix", "global_scale", "apply_unit_scale", "unit_scale",
"bake_space_transform", "global_matrix_inv", "global_matrix_inv_transposed",
Bastien Montagne
committed
"context_objects", "object_types", "use_mesh_modifiers", "use_mesh_modifiers_render",
Bastien Montagne
committed
"mesh_smooth_type", "use_mesh_edges", "use_tspace",
Bastien Montagne
committed
"armature_nodetype", "use_armature_deform_only", "add_leaf_bones",
"bone_correction_matrix", "bone_correction_matrix_inv",
Bastien Montagne
committed
"bake_anim", "bake_anim_use_all_bones", "bake_anim_use_nla_strips", "bake_anim_use_all_actions",
Bastien Montagne
committed
"bake_anim_step", "bake_anim_simplify_factor", "bake_anim_force_startend_keying",
"use_metadata", "media_settings", "use_custom_props",
))
# Helper container gathering some data we need multiple times:
# * templates.
# * settings, scene.
# * objects.
# * object data.
# * skinning data (binding armature/mesh).
# * animations.
Jens Ch. Restemeier
committed
FBXExportData = namedtuple("FBXExportData", (
"templates", "templates_users", "connections",
Bastien Montagne
committed
"settings", "scene", "objects", "animations", "animated", "frame_start", "frame_end",
"data_empties", "data_lamps", "data_cameras", "data_meshes", "mesh_mat_indices",
Bastien Montagne
committed
"data_bones", "data_leaf_bones", "data_deformers_skin", "data_deformers_shape",
"data_world", "data_materials", "data_textures", "data_videos",
))
Jens Ch. Restemeier
committed
# Helper container gathering all importer settings.
FBXImportSettings = namedtuple("FBXImportSettings", (
"report", "to_axes", "global_matrix", "global_scale",
Bastien Montagne
committed
"bake_space_transform", "global_matrix_inv", "global_matrix_inv_transposed",
"use_custom_normals", "use_cycles", "use_image_search",
Jens Ch. Restemeier
committed
"use_alpha_decals", "decal_offset",
"use_anim", "anim_offset",
"use_custom_props", "use_custom_props_enum_as_string",
Bastien Montagne
committed
"cycles_material_wrap_map", "image_cache",
Bastien Montagne
committed
"ignore_leaf_bones", "force_connect_children", "automatic_bone_orientation", "bone_correction_matrix",
"use_prepost_rot",