[Bf-extensions-cvs] [99dcf0d] fbx_io_development: FBX export: first attempt to support shape keys.
Bastien Montagne
noreply at git.blender.org
Wed Jun 11 16:51:24 CEST 2014
Commit: 99dcf0d273e05333940a77c305fb7a3ab6e0ba55
Author: Bastien Montagne
Date: Wed Jun 11 16:47:00 2014 +0200
https://developer.blender.org/rBA99dcf0d273e05333940a77c305fb7a3ab6e0ba55
FBX export: first attempt to support shape keys.
Note: though produced 'code' looks OK, it seems broken (crashes Unity, and also fails in converter).
Maybe we need the 'fake anim' (i.e. with stack, layer and curvenodes, but *without* curves, though
their uuid are listed in connections !!!!) generated by converter from a collada export?
I can’t say how much sick I am of this "format"... Each time I add something it turns into
a dirty blind fight in pitch black room.
===================================================================
M io_scene_fbx/export_fbx_bin.py
M io_scene_fbx/fbx_utils.py
===================================================================
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index c902a66..dd2cbac 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -42,6 +42,7 @@ from .fbx_utils import (
FBX_GEOMETRY_VERSION, FBX_GEOMETRY_NORMAL_VERSION, FBX_GEOMETRY_BINORMAL_VERSION, FBX_GEOMETRY_TANGENT_VERSION,
FBX_GEOMETRY_SMOOTHING_VERSION, FBX_GEOMETRY_VCOLOR_VERSION, FBX_GEOMETRY_UV_VERSION,
FBX_GEOMETRY_MATERIAL_VERSION, FBX_GEOMETRY_LAYER_VERSION,
+ FBX_GEOMETRY_SHAPE_VERSION, FBX_DEFORMER_SHAPE_VERSION, FBX_DEFORMER_SHAPECHANNEL_VERSION,
FBX_POSE_BIND_VERSION, FBX_DEFORMER_SKIN_VERSION, FBX_DEFORMER_CLUSTER_VERSION,
FBX_MATERIAL_VERSION, FBX_TEXTURE_VERSION,
FBX_ANIM_KEY_VERSION,
@@ -50,13 +51,14 @@ from .fbx_utils import (
FBX_LIGHT_TYPES, FBX_LIGHT_DECAY_TYPES,
RIGHT_HAND_AXES, FBX_FRAMERATES,
# Miscellaneous utils.
- units_convert, units_convert_iter, matrix_to_array, similar_values,
+ units_convert, units_convert_iter, matrix_to_array, similar_values, similar_values_iter,
# UUID from key.
get_fbx_uuid_from_key,
# Key generators.
get_blenderID_key, get_blenderID_name,
+ get_blender_mesh_shape_key, get_blender_mesh_shape_channel_key,
get_blender_empty_key, get_blender_bone_key,
- get_blender_armature_bindpose_key, get_blender_armature_skin_key, get_blender_bone_cluster_key,
+ get_blender_bindpose_key, get_blender_armature_skin_key, get_blender_bone_cluster_key,
get_blender_anim_id_base, get_blender_anim_stack_key, get_blender_anim_layer_key,
get_blender_anim_curve_node_key, get_blender_anim_curve_key,
# FBX element data.
@@ -655,6 +657,102 @@ def fbx_data_camera_elements(root, cam_obj, scene_data):
elem_data_single_float64(cam, b"CameraOrthoZoom", 1.0)
+def fbx_data_bindpose_element(root, me_obj, me, scene_data, arm_obj=None, bones=[]):
+ """
+ Helper, since bindpose are used by both meshes shape keys and armature bones...
+ """
+ if arm_obj is None:
+ arm_obj = me_obj
+ # We assume bind pose for our bones are their "Editmode" pose...
+ # All matrices are expected in global (world) space.
+ bindpose_key = get_blender_bindpose_key(arm_obj.bdata, me)
+ fbx_pose = elem_data_single_int64(root, b"Pose", get_fbx_uuid_from_key(bindpose_key))
+ fbx_pose.add_string(fbx_name_class(me.name.encode(), b"Pose"))
+ fbx_pose.add_string(b"BindPose")
+
+ elem_data_single_string(fbx_pose, b"Type", b"BindPose")
+ elem_data_single_int32(fbx_pose, b"Version", FBX_POSE_BIND_VERSION)
+ elem_data_single_int32(fbx_pose, b"NbPoseNodes", 1 + len(bones))
+
+ # First node is mesh/object.
+ mat_world_obj = me_obj.fbx_object_matrix(scene_data, global_space=True)
+ fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
+ elem_data_single_int64(fbx_posenode, b"Node", me_obj.fbx_uuid)
+ elem_data_single_float64_array(fbx_posenode, b"Matrix", matrix_to_array(mat_world_obj))
+ # And all bones of armature!
+ mat_world_bones = {}
+ for bo_obj in bones:
+ bomat = bo_obj.fbx_object_matrix(scene_data, rest=True, global_space=True)
+ mat_world_bones[bo_obj] = bomat
+ fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
+ elem_data_single_int64(fbx_posenode, b"Node", bo_obj.fbx_uuid)
+ elem_data_single_float64_array(fbx_posenode, b"Matrix", matrix_to_array(bomat))
+
+ return mat_world_obj, mat_world_bones
+
+
+def fbx_data_mesh_shapes_elements(root, me_obj, me, scene_data, fbx_me_tmpl, fbx_me_props):
+ """
+ Write shape keys related data.
+ """
+ if me not in scene_data.data_deformers_shape:
+ return
+
+ # First, write the geometry data itself (i.e. shapes).
+ _me_key, shape_key, shapes = scene_data.data_deformers_shape[me]
+
+ channels = []
+
+ for shape, (channel_key, geom_key) in shapes.items():
+ # Only write vertices really different from org coordinates!
+ shape_verts_co = []
+ shape_verts_idx = []
+ for idx, (sv, v) in enumerate(zip(shape.data, me.vertices)):
+ if similar_values_iter(sv.co, v.co):
+ continue
+ shape_verts_co.extend(sv.co);
+ shape_verts_idx.append(idx);
+ shape_verts_weights = [100.0] * (len(shape_verts_co) // 3) # for now, we can use vgroup later here!
+ channels.append((channel_key, shape, shape_verts_weights))
+
+ geom = elem_data_single_int64(root, b"Geometry", get_fbx_uuid_from_key(geom_key))
+ geom.add_string(fbx_name_class(shape.name.encode(), b"Geometry"))
+ geom.add_string(b"Shape")
+
+ tmpl = elem_props_template_init(scene_data.templates, b"Geometry")
+ props = elem_properties(geom)
+ elem_props_template_finalize(tmpl, props)
+
+ elem_data_single_int32(geom, b"Version", FBX_GEOMETRY_SHAPE_VERSION)
+
+ elem_data_single_int32_array(geom, b"Indexes", shape_verts_idx)
+ elem_data_single_float64_array(geom, b"Vertices", shape_verts_co)
+ elem_data_single_float64_array(geom, b"Normals", [0.0] * len(shape_verts_co))
+
+ # Yiha! BindPose for shapekeys too! Dodecasigh...
+ # XXX Not sure yet whether several bindposes on same mesh are allowed, or not... :/
+ fbx_data_bindpose_element(root, me_obj, me, scene_data)
+
+ # ...and now, the deformers stuff.
+ fbx_shape = elem_data_single_int64(root, b"Deformer", get_fbx_uuid_from_key(shape_key))
+ fbx_shape.add_string(fbx_name_class(me.name.encode(), b"Deformer"))
+ fbx_shape.add_string(b"BlendShape")
+
+ elem_data_single_int32(fbx_shape, b"Version", FBX_DEFORMER_SHAPE_VERSION)
+
+ for channel_key, shape, shape_verts_weights in channels:
+ fbx_channel = elem_data_single_int64(root, b"Deformer", get_fbx_uuid_from_key(channel_key))
+ fbx_channel.add_string(fbx_name_class(shape.name.encode(), b"SubDeformer"))
+ fbx_channel.add_string(b"BlendShapeChannel")
+
+ elem_data_single_int32(fbx_channel, b"Version", FBX_DEFORMER_SHAPECHANNEL_VERSION)
+ elem_data_single_float64(fbx_channel, b"DeformPercent", shape.value * 100.0) # Percents...
+ elem_data_single_float64_array(fbx_channel, b"FullWeights", shape_verts_weights)
+
+ # *WHY* add this in linked mesh properties too? *cry*
+ # No idea whether it’s percent here too, or more usual factor :/
+ elem_props_template_set(fbx_me_tmpl, fbx_me_props, "p_number", shape.name.encode(), shape.value, animatable=True)
+
def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
"""
Write the Mesh (Geometry) data block.
@@ -673,7 +771,8 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
# No gscale/gmat here, all data are supposed to be in object space.
smooth_type = scene_data.settings.mesh_smooth_type
- do_bake_space_transform = ObjectWrapper(me_obj).use_bake_space_transform(scene_data)
+ me_obj = ObjectWrapper(me_obj)
+ do_bake_space_transform = me_obj.use_bake_space_transform(scene_data)
# Vertices are in object space, but we are post-multiplying all transforms with the inverse of the
# global matrix, so we need to apply the global matrix to the vertices to get the correct result.
@@ -692,8 +791,6 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
tmpl = elem_props_template_init(scene_data.templates, b"Geometry")
props = elem_properties(geom)
- elem_props_template_finalize(tmpl, props)
-
# Custom properties.
if scene_data.settings.use_custom_properties:
fbx_data_element_custom_properties(props, me)
@@ -1035,6 +1132,10 @@ def fbx_data_mesh_elements(root, me_obj, scene_data, done_meshes):
elem_data_single_string(lay_tan, b"Type", b"LayerElementTangent")
elem_data_single_int32(lay_tan, b"TypedIndex", tspaceidx)
+ # Shape keys...
+ fbx_data_mesh_shapes_elements(root, me_obj, me, scene_data, tmpl, props)
+
+ elem_props_template_finalize(tmpl, props)
done_meshes.add(me_key)
@@ -1240,12 +1341,13 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
if scene_data.settings.use_custom_properties:
fbx_data_element_custom_properties(props, bo)
- # Deformers and BindPoses.
+ # Skin deformers and BindPoses.
# Note: we might also use Deformers for our "parent to vertex" stuff???
- deformer = scene_data.data_deformers.get(arm_obj, None)
+ deformer = scene_data.data_deformers_skin.get(arm_obj, None)
if deformer is not None:
for me, (skin_key, ob_obj, clusters) in deformer.items():
# BindPose.
+ """
# We assume bind pose for our bones are their "Editmode" pose...
# All matrices are expected in global (world) space.
bindpose_key = get_blender_armature_bindpose_key(arm_obj.bdata, me)
@@ -1270,6 +1372,9 @@ def fbx_data_armature_elements(root, arm_obj, scene_data):
fbx_posenode = elem_empty(fbx_pose, b"PoseNode")
elem_data_single_int64(fbx_posenode, b"Node", bo_obj.fbx_uuid)
elem_data_single_float64_array(fbx_posenode, b"Matrix", matrix_to_array(bomat))
+ """
+
+ mat_worl_obj, mat_world_bones = fbx_data_bindpose_element(root, ob_obj, me, scene_data, arm_obj, bones)
# Deformer.
fbx_skin = elem_data_single_int64(root, b"Deformer", get_fbx_uuid_from_key(skin_key))
@@ -1516,7 +1621,7 @@ def fbx_mat_properties_from_texture(tex):
def fbx_skeleton_from_armature(scene, settings, arm_obj, objects, data_meshes,
- data_bones, data_deformers, arm_parents):
+ data_bones, data_deformers_skin, 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).
@@ -1568,8 +1673,8 @@ def fbx_skeleton_from_armature(scene, settings, arm_obj, objects, data_meshes,
# Create skin & clusters relations (note skins are connected to geometry, *not* model!).
_key, me, _free = data_mes
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list