[Bf-extensions-cvs] [3f98b7a] fbx_io_development: FBX import: first basic working anim support.

Bastien Montagne noreply at git.blender.org
Fri May 23 17:49:38 CEST 2014


Commit: 3f98b7a7ee102b8b6c32e2063619c357d226e2b2
Author: Bastien Montagne
Date:   Fri May 23 08:26:15 2014 +0200
https://developer.blender.org/rBA3f98b7a7ee102b8b6c32e2063619c357d226e2b2

FBX import: first basic working anim support.

For now, only loc/rot/scale. Also only objects (trying this code with animated bones
would likely be ugly!).

All curves are currently assumed linear, adding full support for all FBX curves modes
is a huge work, to be done later. So you'd rather 'bake' your animations when exporting
for Blender, for now (as Blender exporter itself does, btw).

Will also generate keyframes for whole loc/rot/scale, since it's pretty hard currently
to guess which channel will actually be animated in Blender, once all FBX transform
magic has been done. This should be enhanceable though (as in exporter).

And finally, no action are assigned to any objects once import is finished (that kind
of info is not really available in FBX), so you'll have to do it by hand to see things
actually moving! :P

Note we assume standard FBX 'actions' starts at KTime 0, while standard Blender actions
start at frame 1.0.

===================================================================

M	io_scene_fbx/import_fbx.py

===================================================================

diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index ea01567..dbc9bee 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -262,16 +262,64 @@ def elem_props_get_enum(elem, elem_prop_id, default=None):
 
 # ------
 # Object
+from collections import namedtuple
 
-def blen_read_object_transform(fbx_props, fbx_obj, rot_alt_mat):
-    # This is quite involved, 'fbxRNode.cpp' from openscenegraph used as a reference
 
+FBXTransformData = namedtuple("FBXTransformData", (
+    "loc",
+    "rot", "rot_ofs", "rot_piv", "pre_rot", "pst_rot", "rot_ord", "rot_alt_mat",
+    "sca", "sca_ofs", "sca_piv",
+))
+
+
+object_tdata_cache = {}
+
+
+def blen_read_object_transform_do(transform_data):
+    from mathutils import Matrix, Euler
+
+    # translation
+    lcl_translation = Matrix.Translation(transform_data.loc)
+
+    # rotation
+    lcl_rot = Euler(tuple_deg_to_rad(transform_data.rot), transform_data.rot_ord).to_matrix().to_4x4()
+    lcl_rot = lcl_rot * transform_data.rot_alt_mat
+    pre_rot = Euler(tuple_deg_to_rad(transform_data.pre_rot), transform_data.rot_ord).to_matrix().to_4x4()
+    pst_rot = Euler(tuple_deg_to_rad(transform_data.pst_rot), transform_data.rot_ord).to_matrix().to_4x4()
+
+    rot_ofs = Matrix.Translation(transform_data.rot_ofs)
+    rot_piv = Matrix.Translation(transform_data.rot_piv)
+    sca_ofs = Matrix.Translation(transform_data.sca_ofs)
+    sca_piv = Matrix.Translation(transform_data.sca_piv)
+
+    # scale
+    lcl_scale = Matrix()
+    lcl_scale[0][0], lcl_scale[1][1], lcl_scale[2][2] = transform_data.sca
+
+    return (
+        lcl_translation *
+        rot_ofs *
+        rot_piv *
+        pre_rot *
+        lcl_rot *
+        pst_rot *
+        rot_piv.inverted() *
+        sca_ofs *
+        sca_piv *
+        lcl_scale *
+        sca_piv.inverted()
+    )
+
+
+def blen_read_object_transform_preprocess(fbx_props, fbx_obj, rot_alt_mat):
+    # This is quite involved, 'fbxRNode.cpp' from openscenegraph used as a reference
     const_vector_zero_3d = 0.0, 0.0, 0.0
     const_vector_one_3d = 1.0, 1.0, 1.0
+    data = {}
 
-    loc = elem_props_get_vector_3d(fbx_props, b'Lcl Translation', const_vector_zero_3d)
-    rot = elem_props_get_vector_3d(fbx_props, b'Lcl Rotation', const_vector_zero_3d)
-    sca = elem_props_get_vector_3d(fbx_props, b'Lcl Scaling', const_vector_one_3d)
+    loc = list(elem_props_get_vector_3d(fbx_props, b'Lcl Translation', const_vector_zero_3d))
+    rot = list(elem_props_get_vector_3d(fbx_props, b'Lcl Rotation', const_vector_zero_3d))
+    sca = list(elem_props_get_vector_3d(fbx_props, b'Lcl Scaling', const_vector_one_3d))
 
     rot_ofs = elem_props_get_vector_3d(fbx_props, b'RotationOffset', const_vector_zero_3d)
     rot_piv = elem_props_get_vector_3d(fbx_props, b'RotationPivot', const_vector_zero_3d)
@@ -297,38 +345,9 @@ def blen_read_object_transform(fbx_props, fbx_obj, rot_alt_mat):
         pst_rot = const_vector_zero_3d
         rot_ord = 'XYZ'
 
-    from mathutils import Matrix, Euler
-
-    # translation
-    lcl_translation = Matrix.Translation(loc)
-
-    # rotation
-    lcl_rot = Euler(tuple_deg_to_rad(rot), rot_ord).to_matrix().to_4x4() * rot_alt_mat
-    pre_rot = Euler(tuple_deg_to_rad(pre_rot), rot_ord).to_matrix().to_4x4()
-    pst_rot = Euler(tuple_deg_to_rad(pst_rot), rot_ord).to_matrix().to_4x4()
-
-    rot_ofs = Matrix.Translation(rot_ofs)
-    rot_piv = Matrix.Translation(rot_piv)
-    sca_ofs = Matrix.Translation(sca_ofs)
-    sca_piv = Matrix.Translation(sca_piv)
-
-    # scale
-    lcl_scale = Matrix()
-    lcl_scale[0][0], lcl_scale[1][1], lcl_scale[2][2] = sca
-
-    return (
-        lcl_translation *
-        rot_ofs *
-        rot_piv *
-        pre_rot *
-        lcl_rot *
-        pst_rot *
-        rot_piv.inverted() *
-        sca_ofs *
-        sca_piv *
-        lcl_scale *
-        sca_piv.inverted()
-    )
+    return FBXTransformData(loc,
+                            rot, rot_ofs, rot_piv, pre_rot, pst_rot, rot_ord, rot_alt_mat,
+                            sca, sca_ofs, sca_piv)
 
 
 def blen_read_object(fbx_tmpl, fbx_obj, object_data):
@@ -360,7 +379,12 @@ def blen_read_object(fbx_tmpl, fbx_obj, object_data):
     else:
         rot_alt_mat = Matrix()
 
-    obj.matrix_basis = blen_read_object_transform(fbx_props, fbx_obj, rot_alt_mat)
+    if obj not in object_tdata_cache:
+        transform_data = blen_read_object_transform_preprocess(fbx_props, fbx_obj, rot_alt_mat)
+        object_tdata_cache[obj] = transform_data
+    else:
+        transform_data = object_tdata_cache[obj]
+    obj.matrix_basis = blen_read_object_transform_do(transform_data)
 
     return obj
 
@@ -508,7 +532,12 @@ def blen_read_armatures(fbx_tmpl, armatures, scene, global_matrix):
             assert(fbx_props[0] is not None)
 
             pbo = b_item[1] = bl_adata.pose.bones[bl_bname]
-            mat = blen_read_object_transform(fbx_props, fbx_bdata, Matrix())
+            if pbo not in object_tdata_cache:
+                transform_data = blen_read_object_transform_preprocess(fbx_props, fbx_bdata, Matrix())
+                object_tdata_cache[pbo] = transform_data
+            else:
+                transform_data = object_tdata_cache[pbo]
+            mat = blen_read_object_transform_do(transform_data)
             if pbo.parent:
                 # Bring back matrix in armature space.
                 mat = pbo.parent.matrix * mat
@@ -517,9 +546,10 @@ def blen_read_armatures(fbx_tmpl, armatures, scene, global_matrix):
 
 # ---------
 # Animation
-def blen_read_animations_curve_baker(fbx_acdata, blen_start_offset, fbx_start_offset, fps):
+def blen_read_animations_curves_iter(curves, blen_start_offset, fbx_start_offset, fps):
     """
-    Get raw FBX AnimCurve data and output a baked array of "baked" float values (one per frame).
+    Get raw FBX AnimCurve list, and yield values for all curves at each singular curves' keyframes,
+    together with (blender) timing, in frames.
     blen_start_offset is expected in frames, while fbx_start_offset is expected in FBX ktime.
     """
     # As a first step, assume linear interpolation between key frames, we'll (try to!) handle more
@@ -527,51 +557,120 @@ def blen_read_animations_curve_baker(fbx_acdata, blen_start_offset, fbx_start_of
     from .fbx_utils import FBX_KTIME
     timefac = fps / FBX_KTIME
 
-    fbx_ktime = elem_prop_first(elem_find_first(fbx_acdata, b'KeyTime'))
-    fbx_kval = elem_prop_first(elem_find_first(fbx_acdata, b'KeyValueFloat'))
-    assert(len(fbx_ktime) == len(fbx_kval))
-
-    keyframes = []
-
-    curr_blenframe = 0.0
-    curr_fbxkey = 0
-    max_fbxkey = len(fbx_ktime) - 1
-    while 1:
-        curr_fbxktime = curr_blenframe / timefac
-        curr_fbxkey_ktime = fbx_ktime[curr_fbxkey] - fbx_start_offset
-        curr_fbxkey_val = fbx_kval[curr_fbxkey]
-        if curr_fbxktime >= curr_fbxkey_ktime:
-            # Add exact fbx key and increment current fbx key index.
-            keyframes.append((curr_fbxkey_ktime * timefac + blen_start_offset, curr_fbxkey_val))
-            curr_fbxkey += 1
-            if curr_fbxkey > max_fbxkey:
-                break  # We are done!
-        elif curr_fbxkey != 0:
-            prev_fbxkey_ktime = fbx_ktime[curr_fbxkey - 1] - fbx_start_offset
-            prev_fbxkey_val = fbx_kval[curr_fbxkey - 1]  # Not an issue in case curr_fbxkey = 0, we won't use it!
-            ifac = (curr_fbxktime - prev_fbxkey_ktime) / (curr_fbxkey_ktime - prev_fbxkey_ktime)
-            ival = (curr_fbxkey_val - prev_fbxkey_val) * ifac + prev_fbxkey_val
-            keyframes.append((curr_blenframe + blen_start_offset, ival))
-        # Only increment curr_blenframe if current fbx key is ahead of it in time.
-        if curr_fbxktime <= curr_fbxkey_ktime:
-            curr_blenframe += 1.0
-
-    return keyframes
+    fbx_curves = tuple([0,
+                        elem_prop_first(elem_find_first(c[2], b'KeyTime')),
+                        elem_prop_first(elem_find_first(c[2], b'KeyValueFloat'))]
+                       for c in curves)
+
+    while True:
+        tmin = min(fbx_curves, key=lambda e: e[1][e[0]])
+        curr_fbxktime = tmin[1][tmin[0]]
+        curr_values = []
+        do_break = True
+        for item in fbx_curves:
+            idx, times, values = item
+            if idx != -1:
+                do_break = False
+            if times[idx] > curr_fbxktime:
+                if idx == 0:
+                    curr_values.append(values[idx])
+                else:
+                    # Interpolate between this key and the previous one.
+                    ifac = (curr_fbxktime - times[idx - 1]) / (times[idx] - times[idx - 1])
+                    curr_values.append((values[idx] - values[idx - 1]) * ifac + values[idx - 1])
+            else:
+                curr_values.append(values[idx])
+                if idx >= 0:
+                    idx += 1
+                    if idx >= len(times):
+                        # We have reached our last element for this curve, stay on it from now on...
+                        idx = -1
+                    item[0] = idx
+        curr_blenkframe = (curr_fbxktime - fbx_start_offset) * timefac + blen_start_offset
+        yield (curr_blenkframe, curr_values)
+        if do_break:
+            break
+
+
+def blen_read_animations_action(action, ob, cnodes, global_matrix, fps):
+    """
+    'Bake' loc/rot/scale into the action, taking into account global_matrix if no parent is present.
+    """
+    from mathutils import Euler, Matrix
+    from itertools import chain
+
+    if ob not in object_tdata_cache:
+        printf("ERROR! object '%s' has no transform data, while being animated!" % ob.name)
+
+    fbx_curves = []
+    # Since we might get other channels animated in the end, due to all FBX transform magic,
+    # we need to add curves for whole loc/rot/scale in any case.
+    rot_mode = ob.rotation_mode
+    if rot_mode == 'QUATERNION':
+        obprops = (("location", 3), ("rotation_quaternion", 4), ("scale", 3))
+    elif rot_mode == 'AXIS_ANGLE':
+        obprops = (("location", 3), ("rotation_axis_angle", 4), ("scale", 3))
+    else:  # Euler
+        obprops = (("location", 3), ("

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list