[Bf-extensions-cvs] [866397a] master: FBX Exporter: (Optionally) add leaf bones and change bone orientation
Bastien Montagne
noreply at git.blender.org
Mon Sep 8 15:02:04 CEST 2014
Commit: 866397a8ffb8c1bc994b29de9f78059215bcd57d
Author: Bastien Montagne
Date: Mon Sep 8 14:47:30 2014 +0200
Branches: master
https://developer.blender.org/rBA866397a8ffb8c1bc994b29de9f78059215bcd57d
FBX Exporter: (Optionally) add leaf bones and change bone orientation
Revision: D735
Patch by jrestemeier (Jens Restemeier).
===================================================================
M io_scene_fbx_experimental/__init__.py
M io_scene_fbx_experimental/export_fbx_bin.py
M io_scene_fbx_experimental/fbx_utils.py
===================================================================
diff --git a/io_scene_fbx_experimental/__init__.py b/io_scene_fbx_experimental/__init__.py
index aa10ca8..b1e0777 100644
--- a/io_scene_fbx_experimental/__init__.py
+++ b/io_scene_fbx_experimental/__init__.py
@@ -317,6 +317,35 @@ class ExportFBX_experimental(bpy.types.Operator, ExportHelper):
description="Export custom properties",
default=False,
)
+ add_leaf_bones = BoolProperty(
+ name="Add leaf bones",
+ description=("Append a last bone to the end of each chain to specify bone length. It is useful to, "
+ "enable this when exporting into another modelling application and to disable this when"
+ "exporting into a game engine or real-time viewer."),
+ default=True # False for commit!
+ )
+ primary_bone_axis = EnumProperty(
+ name="Primary Bone Axis",
+ items=(('X', "X Axis", ""),
+ ('Y', "Y Axis", ""),
+ ('Z', "Z Axis", ""),
+ ('-X', "-X Axis", ""),
+ ('-Y', "-Y Axis", ""),
+ ('-Z', "-Z Axis", ""),
+ ),
+ default='Y',
+ )
+ secondary_bone_axis = EnumProperty(
+ name="Secondary Bone Axis",
+ items=(('X', "X Axis", ""),
+ ('Y', "Y Axis", ""),
+ ('Z', "Z Axis", ""),
+ ('-X', "-X Axis", ""),
+ ('-Y', "-Y Axis", ""),
+ ('-Z', "-Z Axis", ""),
+ ),
+ default='X',
+ )
use_armature_deform_only = BoolProperty(
name="Only Deform Bones",
description="Only write deforming bones (and non-deforming ones when they have deforming children)",
@@ -431,6 +460,9 @@ class ExportFBX_experimental(bpy.types.Operator, ExportHelper):
layout.prop(self, "use_armature_deform_only")
if is_74bin:
layout.prop(self, "use_custom_props")
+ layout.prop(self, "add_leaf_bones")
+ layout.prop(self, "primary_bone_axis")
+ layout.prop(self, "secondary_bone_axis")
layout.prop(self, "bake_anim")
col = layout.column()
col.enabled = self.bake_anim
diff --git a/io_scene_fbx_experimental/export_fbx_bin.py b/io_scene_fbx_experimental/export_fbx_bin.py
index d8c6438..4920d2b 100644
--- a/io_scene_fbx_experimental/export_fbx_bin.py
+++ b/io_scene_fbx_experimental/export_fbx_bin.py
@@ -1379,6 +1379,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
@@ -1390,13 +1392,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
+ # (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)
@@ -1717,6 +1724,90 @@ 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 = {bone: 0 for bone,_ in data_bones.items()}
+ for bone,_ in data_bones.items():
+ if bone.parent and bone.parent._tag == 'BO':
+ child_count[bone.parent] += 1
+
+ bone_radius_scale = settings.global_scale * 33.0
+
+ # generate bone data
+ leaf_parents = [bone for bone,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.bdata.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_write_leaf_bone_data(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, _, node_uuid, _, matrix, hide, _) in scene_data.data_leaf_bones:
+ 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)
+
+ for (node_name, _, _, attr_uuid, _, _, size) in scene_data.data_leaf_bones:
+ 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)
+
+def fbx_write_leaf_bone_connections(connections, leaf_bones):
+ # attach nodes to parents and attributes to nodes
+ for (_, parent_uuid, node_uuid, attr_uuid, _, _, _) in leaf_bones:
+ connections.append((b"OO", node_uuid, parent_uuid, None))
+ connections.append((b"OO", attr_uuid, node_uuid, None))
+
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.
@@ -2049,6 +2140,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)),))
@@ -2126,6 +2222,7 @@ def fbx_data_from_scene(scene, settings):
data_empties, data_lamps, data_cameras, data_meshes, None,
data_bones, data_deformers_skin, data_deformers_shape,
data_world, data_materials, data_textures, data_videos,
+ data_leaf_bones
)
animations, frame_start, frame_end = fbx_animations(tmp_scdata)
@@ -2247,6 +2344,10 @@ 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
+ if data_leaf_bones:
+ fbx_write_leaf_bone_connections(connections, data_leaf_bones)
+
# 'Shape' deformers (shape keys, only for meshes currently)...
for me_key, shapes_key, shapes in data_deformers_shape.values():
# shape -> geometry
@@ -2333,6 +2434,7 @@ def fbx_data_from_scene(scene, settings):
data_empties, data_lamps, data_cameras, data_meshes, mesh_mat_indices,
data_bones, data_deformers_skin,
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list