Newer
Older
return ObjectWrapper(bo_par, self.bdata.parent)
else: # Fallback to mere object parenting.
return ObjectWrapper(self.bdata.parent)
else:
# Mere object parenting.
return ObjectWrapper(self.bdata.parent)
elif self._tag == 'DP':
else: # self._tag == 'BO'
return ObjectWrapper(self.bdata.parent, self._ref) or ObjectWrapper(self._ref)
parent = property(get_parent)
def get_bdata_pose_bone(self):
if self._tag == 'BO':
return self._ref.pose.bones[self.bdata.name]
return None
bdata_pose_bone = property(get_bdata_pose_bone)
def get_matrix_local(self):
if self._tag == 'OB':
return self.bdata.matrix_local.copy()
elif self._tag == 'DP':
return self._ref.matrix_world.inverted_safe() @ self._dupli_matrix
else: # 'BO', current pose
# PoseBone.matrix is in armature space, bring in back in real local one!
par = self.bdata.parent
par_mat_inv = self._ref.pose.bones[par.name].matrix.inverted_safe() if par else Matrix()
return par_mat_inv @ self._ref.pose.bones[self.bdata.name].matrix
matrix_local = property(get_matrix_local)
def get_matrix_global(self):
if self._tag == 'OB':
return self.bdata.matrix_world.copy()
elif self._tag == 'DP':
return self._dupli_matrix
else: # 'BO', current pose
return self._ref.matrix_world @ self._ref.pose.bones[self.bdata.name].matrix
matrix_global = property(get_matrix_global)
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
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
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
Bastien Montagne
committed
# Apply the bone correction.
if scene_data.settings.bone_correction_matrix:
matrix = matrix @ scene_data.settings.bone_correction_matrix
elif self.bdata.type == 'LIGHT':
elif self.bdata.type == '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
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
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
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
if self._tag == 'OB' and self.bdata.is_instancer:
return (ObjectWrapper(dup) for dup in depsgraph.object_instances
if dup.parent and ObjectWrapper(dup.parent.original) == self)
return ()
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",
"mesh_smooth_type", "use_subsurf", "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",
"settings", "scene", "depsgraph", "objects", "animations", "animated", "frame_start", "frame_end",
"data_empties", "data_lights", "data_cameras", "data_meshes", "mesh_material_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_image_search",
Jens Ch. Restemeier
committed
"use_alpha_decals", "decal_offset",
"use_anim", "anim_offset",
"use_subsurf",
"use_custom_props", "use_custom_props_enum_as_string",
"nodal_material_wrap_map", "image_cache",
Bastien Montagne
committed
"ignore_leaf_bones", "force_connect_children", "automatic_bone_orientation", "bone_correction_matrix",
"use_prepost_rot",