Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Montagne <montagne29@wanadoo.fr>2014-10-08 17:33:56 +0400
committerBastien Montagne <montagne29@wanadoo.fr>2014-10-08 17:42:44 +0400
commit4a07e805addbe606d80223811d6d29c68579b603 (patch)
tree7a73c681cd56512a6c35f69adc7bebc190cd5d84 /io_scene_fbx/export_fbx_bin.py
parent355f86ca0881785af1b4362b3e7fb6a8ffdbd6f2 (diff)
Merged experimental code in 'stable' addon (mainly Jens Restemeier's summer work).
Will remove experimental one soon. Keeping old 6.1 exporter active for now, though. Notes: * Not 100% happy with how complex code is now... Rather sure though refactor would take a huge amount of time, not worth it for now. * New code handles things like armature & animation import much much better than previous one, so definitively worth it. * New code also fixes some real bugs (like Blender-side of T42135). * Did a few cosmetic changes on the run, nothing serious. This should make FBX work complete, aside from a few nice TODOs that I'll tackle in comming weeks, in addition to ongoing bugbusting. Many thanks to Jens for his work on armature mess! :D
Diffstat (limited to 'io_scene_fbx/export_fbx_bin.py')
-rw-r--r--io_scene_fbx/export_fbx_bin.py134
1 files changed, 130 insertions, 4 deletions
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index ff223fa8..a5cfdd25 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -1371,6 +1371,8 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
mat_world_arm = arm_obj.fbx_object_matrix(scene_data, global_space=True)
bones = tuple(bo_obj for bo_obj in arm_obj.bones if bo_obj in scene_data.objects)
+ bone_radius_scale = scene_data.settings.global_scale * 33.0
+
# Bones "data".
for bo_obj in bones:
bo = bo_obj.bdata
@@ -1382,13 +1384,18 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
tmpl = elem_props_template_init(scene_data.templates, b"Bone")
props = elem_properties(fbx_bo)
- elem_props_template_set(tmpl, props, "p_double", b"Size", (bo.tail_local - bo.head_local).length)
+ elem_props_template_set(tmpl, props, "p_double", b"Size", bo.head_radius * bone_radius_scale)
elem_props_template_finalize(tmpl, props)
# Custom properties.
if scene_data.settings.use_custom_props:
fbx_data_element_custom_properties(props, bo)
+ # Store Blender bone length - XXX Not much useful actually :/
+ # (LimbLength can't be used because it is a scale factor 0-1 for the parent-child distance:
+ # http://docs.autodesk.com/FBX/2014/ENU/FBX-SDK-Documentation/cpp_ref/class_fbx_skeleton.html#a9bbe2a70f4ed82cd162620259e649f0f )
+ # elem_props_set(props, "p_double", "BlenderBoneLength".encode(), (bo.tail_local - bo.head_local).length, custom=True)
+
# Skin deformers and BindPoses.
# Note: we might also use Deformers for our "parent to vertex" stuff???
deformer = scene_data.data_deformers_skin.get(arm_obj, None)
@@ -1450,6 +1457,56 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
elem_data_single_float64_array(fbx_clstr, b"TransformAssociateModel", matrix4_to_array(mat_world_arm))
+def fbx_data_leaf_bone_elements(root, scene_data):
+ # Write a dummy leaf bone that is used by applications to show the length of the last bone in a chain
+ for (node_name, _par_uuid, node_uuid, attr_uuid, matrix, hide, size) in scene_data.data_leaf_bones:
+ # Bone 'data'...
+ fbx_bo = elem_data_single_int64(root, b"NodeAttribute", attr_uuid)
+ fbx_bo.add_string(fbx_name_class(node_name.encode(), b"NodeAttribute"))
+ fbx_bo.add_string(b"LimbNode")
+ elem_data_single_string(fbx_bo, b"TypeFlags", b"Skeleton")
+
+ tmpl = elem_props_template_init(scene_data.templates, b"Bone")
+ props = elem_properties(fbx_bo)
+ elem_props_template_set(tmpl, props, "p_double", b"Size", size)
+ elem_props_template_finalize(tmpl, props)
+
+ # And bone object.
+ model = elem_data_single_int64(root, b"Model", node_uuid)
+ model.add_string(fbx_name_class(node_name.encode(), b"Model"))
+ model.add_string(b"LimbNode")
+
+ elem_data_single_int32(model, b"Version", FBX_MODELS_VERSION)
+
+ # Object transform info.
+ loc, rot, scale = matrix.decompose()
+ rot = rot.to_euler('XYZ')
+ rot = tuple(convert_rad_to_deg_iter(rot))
+
+ tmpl = elem_props_template_init(scene_data.templates, b"Model")
+ # For now add only loc/rot/scale...
+ props = elem_properties(model)
+ elem_props_template_set(tmpl, props, "p_lcl_translation", b"Lcl Translation", loc)
+ elem_props_template_set(tmpl, props, "p_lcl_rotation", b"Lcl Rotation", rot)
+ elem_props_template_set(tmpl, props, "p_lcl_scaling", b"Lcl Scaling", scale)
+ elem_props_template_set(tmpl, props, "p_visibility", b"Visibility", float(not hide))
+
+ # Absolutely no idea what this is, but seems mandatory for validity of the file, and defaults to
+ # invalid -1 value...
+ elem_props_template_set(tmpl, props, "p_integer", b"DefaultAttributeIndex", 0)
+
+ elem_props_template_set(tmpl, props, "p_enum", b"InheritType", 1) # RSrs
+
+ # Those settings would obviously need to be edited in a complete version of the exporter, may depends on
+ # object type, etc.
+ elem_data_single_int32(model, b"MultiLayer", 0)
+ elem_data_single_int32(model, b"MultiTake", 0)
+ elem_data_single_bool(model, b"Shading", True)
+ elem_data_single_string(model, b"Culling", b"CullingOff")
+
+ elem_props_template_finalize(tmpl, props)
+
+
def fbx_data_object_elements(root, ob_obj, scene_data):
"""
Write the Object (Model) data blocks.
@@ -1709,6 +1766,37 @@ def fbx_skeleton_from_armature(scene, settings, arm_obj, objects, data_meshes,
objects.update(bones)
+def fbx_generate_leaf_bones(settings, data_bones):
+ # find which bons have no children
+ child_count = {bo: 0 for bo, _bo_key in data_bones.items()}
+ for bo, _bo_key in data_bones.items():
+ if bo.parent and bo.parent.is_bone:
+ child_count[bo.parent] += 1
+
+ bone_radius_scale = settings.global_scale * 33.0
+
+ # generate bone data
+ leaf_parents = [bo for bo, count in child_count.items() if count == 0]
+ leaf_bones = []
+ for parent in leaf_parents:
+ node_name = parent.name + "_end"
+ parent_uuid = parent.fbx_uuid
+ node_uuid = get_fbx_uuid_from_key(node_name + "_node")
+ attr_uuid = get_fbx_uuid_from_key(node_name + "_nodeattr")
+
+ hide = parent.hide
+ size = parent.bdata.head_radius * bone_radius_scale
+ bone_length = (parent.bdata.tail_local - parent.bdata.head_local).length
+ matrix = Matrix.Translation((0, bone_length, 0))
+ if settings.bone_correction_matrix_inv:
+ matrix = settings.bone_correction_matrix_inv * matrix
+ if settings.bone_correction_matrix:
+ matrix = matrix * settings.bone_correction_matrix
+ leaf_bones.append((node_name, parent_uuid, node_uuid, attr_uuid, matrix, hide, size))
+
+ return leaf_bones
+
+
def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=None, force_keep=False):
"""
Generate animation data (a single AnimStack) from objects, for a given frame range.
@@ -2050,6 +2138,11 @@ def fbx_data_from_scene(scene, settings):
fbx_skeleton_from_armature(scene, settings, ob_obj, objects, data_meshes,
data_bones, data_deformers_skin, arm_parents)
+ # Generate leaf bones
+ data_leaf_bones = None
+ if settings.add_leaf_bones:
+ data_leaf_bones = fbx_generate_leaf_bones(settings, data_bones)
+
# Some world settings are embedded in FBX materials...
if scene.world:
data_world = OrderedDict(((scene.world, get_blenderID_key(scene.world)),))
@@ -2125,7 +2218,7 @@ def fbx_data_from_scene(scene, settings):
None, None, None,
settings, scene, objects, None, 0.0, 0.0,
data_empties, data_lamps, data_cameras, data_meshes, None,
- data_bones, data_deformers_skin, data_deformers_shape,
+ data_bones, data_leaf_bones, data_deformers_skin, data_deformers_shape,
data_world, data_materials, data_textures, data_videos,
)
animations, frame_start, frame_end = fbx_animations(tmp_scdata)
@@ -2248,6 +2341,11 @@ def fbx_data_from_scene(scene, settings):
mesh_key, _me, _free = data_meshes[ob_obj]
connections.append((b"OO", get_fbx_uuid_from_key(mesh_key), ob_obj.fbx_uuid, None))
+ # Leaf Bones
+ for (_node_name, par_uuid, node_uuid, attr_uuid, _matrix, _hide, _size) in data_leaf_bones:
+ connections.append((b"OO", node_uuid, parent_uuid, None))
+ connections.append((b"OO", attr_uuid, node_uuid, None))
+
# 'Shape' deformers (shape keys, only for meshes currently)...
for me_key, shapes_key, shapes in data_deformers_shape.values():
# shape -> geometry
@@ -2332,7 +2430,7 @@ def fbx_data_from_scene(scene, settings):
templates, templates_users, connections,
settings, scene, objects, animations, frame_start, frame_end,
data_empties, data_lamps, data_cameras, data_meshes, mesh_mat_indices,
- data_bones, data_deformers_skin, data_deformers_shape,
+ data_bones, data_leaf_bones, data_deformers_skin, data_deformers_shape,
data_world, data_materials, data_textures, data_videos,
)
@@ -2546,6 +2644,9 @@ def fbx_objects_elements(root, scene_data):
continue
fbx_data_armature_elements(objects, ob_obj, scene_data)
+ if scene_data.data_leaf_bones:
+ fbx_data_leaf_bone_elements(objects, scene_data)
+
for mat in scene_data.data_materials:
fbx_data_material_elements(objects, mat, scene_data)
@@ -2608,6 +2709,9 @@ def save_single(operator, scene, filepath="",
bake_anim_use_all_actions=True,
bake_anim_step=1.0,
bake_anim_simplify_factor=1.0,
+ add_leaf_bones=False,
+ primary_bone_axis='Y',
+ secondary_bone_axis='X',
use_metadata=True,
path_mode='AUTO',
use_mesh_edges=True,
@@ -2627,6 +2731,11 @@ def save_single(operator, scene, filepath="",
if 'OTHER' in object_types:
object_types |= BLENDER_OTHER_OBJECT_TYPES
+ # Scale/unit mess. FBX can store the 'reference' unit of a file in its UnitScaleFactor property
+ # (1.0 meaning centimeter, afaik). We use that to reflect user's default unit as set in Blender with scale_length.
+ # However, we always get values in BU (i.e. meters), so we have to reverse-apply that scale in global matrix...
+ if scene.unit_settings.system != 'NONE':
+ global_matrix = global_matrix * Matrix.Scale(1.0 / scene.unit_settings.scale_length, 4)
global_scale = global_matrix.median_scale
global_matrix_inv = global_matrix.inverted()
# For transforming mesh normals.
@@ -2636,6 +2745,19 @@ def save_single(operator, scene, filepath="",
if embed_textures and path_mode != 'COPY':
embed_textures = False
+ # Calcuate bone correction matrix
+ bone_correction_matrix = None # Default is None = no change
+ bone_correction_matrix_inv = None
+ if (primary_bone_axis, secondary_bone_axis) != ('Y', 'X'):
+ from bpy_extras.io_utils import axis_conversion
+ bone_correction_matrix = axis_conversion(from_forward=secondary_bone_axis,
+ from_up=primary_bone_axis,
+ to_forward='X',
+ to_up='Y',
+ ).to_4x4()
+ bone_correction_matrix_inv = bone_correction_matrix.inverted()
+
+
media_settings = FBXExportSettingsMedia(
path_mode,
os.path.dirname(bpy.data.filepath), # base_src
@@ -2650,7 +2772,8 @@ def save_single(operator, scene, filepath="",
operator.report, (axis_up, axis_forward), global_matrix, global_scale,
bake_space_transform, global_matrix_inv, global_matrix_inv_transposed,
context_objects, object_types, use_mesh_modifiers,
- mesh_smooth_type, use_mesh_edges, use_tspace, use_armature_deform_only,
+ mesh_smooth_type, use_mesh_edges, use_tspace,
+ use_armature_deform_only, add_leaf_bones, bone_correction_matrix, bone_correction_matrix_inv,
bake_anim, bake_anim_use_nla_strips, bake_anim_use_all_actions, bake_anim_step, bake_anim_simplify_factor,
False, media_settings, use_custom_props,
)
@@ -2730,6 +2853,9 @@ def defaults_unity3d():
"bake_anim_step": 1.0,
"bake_anim_use_nla_strips": True,
"bake_anim_use_all_actions": True,
+ "add_leaf_bones": False, # Avoid memory/performance cost for something only useful for modelling
+ "primary_bone_axis": 'Y', # Doesn't really matter for Unity, so leave unchanged
+ "secondary_bone_axis": 'X',
"path_mode": 'AUTO',
"embed_textures": False,