[Bf-extensions-cvs] [af0507a] fbx_io_development: FBX export: add shapes anim support.
Bastien Montagne
noreply at git.blender.org
Thu Jul 10 21:41:44 CEST 2014
Commit: af0507aeaaf4f047d16c5144604b21ab218a1e34
Author: Bastien Montagne
Date: Thu Jul 10 21:40:57 2014 +0200
https://developer.blender.org/rBAaf0507aeaaf4f047d16c5144604b21ab218a1e34
FBX export: add shapes anim support.
Code is a bit ugly/messy, but for now will do the trick!
===================================================================
M io_scene_fbx/import_fbx.py
===================================================================
diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index 2e7708d..d4a5378 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -313,7 +313,7 @@ def blen_read_object_transform_do(transform_data):
)
-# XXX This might be week, now that we can add vgroups from both bones and shapes, name collisions become
+# XXX This might be weak, now that we can add vgroups from both bones and shapes, name collisions become
# more likely, will have to make this more robust!!!
def add_vgroup_to_objects(vg_indices, vg_weights, vg_name, objects):
assert(len(vg_indices) == len(vg_weights))
@@ -628,7 +628,7 @@ def blen_read_animations_curves_iter(curves, blen_start_offset, fbx_start_offset
break
-def blen_read_animations_action(action, ob, obpath, grpname, cnodes, global_matrix, fps):
+def blen_read_animations_action_object(action, ob, obpath, grpname, cnodes, global_matrix, fps):
"""
'Bake' loc/rot/scale into the action, taking into account global_matrix if no parent is present.
"""
@@ -704,6 +704,33 @@ def blen_read_animations_action(action, ob, obpath, grpname, cnodes, global_matr
fc.update()
+def blen_read_animations_action_shapes(action, kb, cnodes, fps):
+ """
+ 'Bake' shapekeys' value into the action.
+ """
+ fbx_curves = []
+ kbpath = kb.path_from_id("value")
+
+ blen_curves = [action.fcurves.new(kbpath, 0, "Key")]
+ for acn_uuid, (curves, fbxprop) in cnodes.items():
+ for ac_uuid, ((fbx_acdata, _blen_data), channel) in curves.items():
+ fbx_curves.append((fbxprop, channel, fbx_acdata))
+
+ # We assume for now blen init point is frame 1.0, while FBX ktime init point is 0.
+ for frame, values in blen_read_animations_curves_iter(fbx_curves, 1.0, 0, fps):
+ value = 0.0
+ for v, (fbxprop, channel, _fbx_acdata) in zip(values, fbx_curves):
+ assert(fbxprop == b'DeformPercent')
+ value = v / 100.0
+
+ for fc, v in zip(blen_curves, tuple(value)):
+ fc.keyframe_points.insert(frame, v, {'NEEDED', 'FAST'}).interpolation = 'LINEAR'
+
+ # Since we inserted our keyframes in 'FAST' mode, we have to update the fcurves now.
+ for fc in blen_curves:
+ fc.update()
+
+
def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, global_matrix):
"""
Recreate an action per stack/layer/object combinations.
@@ -712,10 +739,9 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, global
actions = {}
for as_uuid, ((fbx_asdata, _blen_data), alayers) in stacks.items():
stack_name = elem_name_ensure_class(fbx_asdata, b'AnimStack')
- for al_uuid, ((fbx_aldata, _blen_data), objects) in alayers.items():
+ for al_uuid, ((fbx_aldata, _blen_data), objects, shapes) in alayers.items():
layer_name = elem_name_ensure_class(fbx_aldata, b'AnimLayer')
for ob, cnodes in objects.items():
- # We want to create actions for objects, but for bones we 'reuse' armatures' actions!
id_data = ob.id_data
key = (as_uuid, al_uuid, id_data)
action = actions.get(key)
@@ -723,14 +749,26 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, global
action_name = "|".join((id_data.name, stack_name, layer_name))
actions[key] = action = bpy.data.actions.new(action_name)
action.use_fake_user = True
+ # We want to create actions for objects, but for bones we 'reuse' armatures' actions!
if id_data == ob:
obpath = ""
grpname = None
else:
obpath = ob.path_from_id() + "."
grpname = ob.name
- blen_read_animations_action(action, ob, obpath, grpname, cnodes, global_matrix, scene.render.fps)
-
+ blen_read_animations_action_object(action, ob, obpath, grpname, cnodes, global_matrix, scene.render.fps)
+
+ print(shapes)
+ for keyblocks, cnodes in shapes.values():
+ for kb in keyblocks:
+ id_data = kb.id_data
+ key = (as_uuid, al_uuid, id_data)
+ action = actions.get(key)
+ if action is None:
+ action_name = "|".join((id_data.name, stack_name, layer_name))
+ actions[key] = action = bpy.data.actions.new(action_name)
+ action.use_fake_user = True
+ blen_read_animations_action_shapes(action, kb, cnodes, scene.render.fps)
# ----
# Mesh
@@ -1129,6 +1167,8 @@ def blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene, global_matri
assert(len(vgweights) == len(indices) == len(dvcos))
create_vg = bool(set(vgweights) - {1.0})
+ keyblocks = []
+
for me, objects in meshes:
vcos = tuple((idx, me.vertices[idx].co + Vector(dvco)) for idx, dvco in zip(indices, dvcos))
objects = list({blen_o for fbx_o, blen_o in objects})
@@ -1149,6 +1189,10 @@ def blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene, global_matri
add_vgroup_to_objects(indices, vgweights, elem_name_utf8, objects)
kb.vertex_group = elem_name_utf8
+ keyblocks.append(kb)
+
+ return keyblocks
+
# --------
# Material
@@ -1732,7 +1776,7 @@ def load(operator, context, filepath="",
# Now that we have objects...
# I) We can handle shapes.
- shapes = {} # No used yet, will be needed for skeys animations...
+ blend_shape_channels = {} # We do not need Shapes themselves, but BlendShapeChannels, for anim.
def _():
fbx_tmpl = fbx_template_get((b'Geometry', b'KFbxShape'))
@@ -1742,7 +1786,6 @@ def load(operator, context, filepath="",
continue
# shape -> blendshapechannel -> blendshape -> mesh.
- blend_channels = []
for bc_uuid, bc_ctype in fbx_connection_map.get(s_uuid, ()):
if bc_ctype.props[0] != b'OO':
continue
@@ -1777,10 +1820,10 @@ def load(operator, context, filepath="",
objects.append(o_item)
meshes.append((bl_mdata, objects))
# BlendShape deformers are only here to connect BlendShapeChannels to meshes, nothing else to do.
- blend_channels.append((fbx_bcdata, meshes))
- blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene, global_matrix)
- shapes[s_uuid] = blend_channels
+ # keyblocks is a list of tuples (mesh, keyblock) matching that shape/blendshapechannel, for animation.
+ keyblocks = blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene, global_matrix)
+ blend_shape_channels[bc_uuid] = keyblocks
_(); del _
# II) We can finish armatures processing.
@@ -1840,6 +1883,8 @@ def load(operator, context, filepath="",
fbx_tmpl_alayer = fbx_template_get((b'AnimationLayer', b'FbxAnimLayer'))
stacks = {}
+ print(blend_shape_channels)
+
# AnimationStacks.
for as_uuid, fbx_asitem in fbx_table_nodes.items():
fbx_asdata, _blen_data = fbx_asitem
@@ -1863,7 +1908,7 @@ def load(operator, context, filepath="",
continue
for as_uuid in get_astacks_from_alayer(al_uuid):
_fbx_asitem, alayers = stacks[as_uuid]
- alayers[al_uuid] = (fbx_alitem, {})
+ alayers[al_uuid] = (fbx_alitem, {}, {}) # objects and shapes.
# AnimationCurveNodes (also the ones linked to actual animated data!).
curvenodes = {}
@@ -1873,16 +1918,21 @@ def load(operator, context, filepath="",
continue
cnode = curvenodes[acn_uuid] = {}
objs = []
- for ob_uuid, ob_ctype in fbx_connection_map.get(acn_uuid, ()):
- if ob_ctype.props[0] != b'OP':
- continue
- lnk_prop = ob_ctype.props[3]
- if lnk_prop not in {b'Lcl Translation', b'Lcl Rotation', b'Lcl Scaling'}:
+ shps = []
+ for n_uuid, n_ctype in fbx_connection_map.get(acn_uuid, ()):
+ if n_ctype.props[0] != b'OP':
continue
- ob = fbx_table_nodes[ob_uuid][1]
- if ob is None:
- continue
- objs.append((ob, lnk_prop))
+ lnk_prop = n_ctype.props[3]
+ if lnk_prop in {b'Lcl Translation', b'Lcl Rotation', b'Lcl Scaling'}:
+ ob = fbx_table_nodes[n_uuid][1]
+ if ob is None:
+ continue
+ objs.append((ob, lnk_prop))
+ elif lnk_prop == b'DeformPercent': # Shape keys.
+ keyblocks = blend_shape_channels.get(n_uuid)
+ if keyblocks is None:
+ continue
+ shps.append(keyblocks)
for al_uuid, al_ctype in fbx_connection_map.get(acn_uuid, ()):
if al_ctype.props[0] != b'OO':
continue
@@ -1890,11 +1940,13 @@ def load(operator, context, filepath="",
if fbx_aldata is None or fbx_aldata.id != b'AnimationLayer' or fbx_aldata.props[2] != b'':
continue
for as_uuid in get_astacks_from_alayer(al_uuid):
- _fbx_alitem, objects = stacks[as_uuid][1][al_uuid]
+ _fbx_alitem, objects, shapes = stacks[as_uuid][1][al_uuid]
assert(_fbx_alitem == fbx_alitem)
for ob, ob_prop in objs:
# No need to keep curvenode FBX data here, contains nothing useful for us.
objects.setdefault(ob, {})[acn_uuid] = (cnode, ob_prop)
+ print(shps)
+ shapes[acn_uuid] = [(keyblocks, cnode) for keyblocks in shps]
# AnimationCurves (real animation data).
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-extensions-cvs
mailing list