FBX IO: Speed up animation import using NumPy #104856
@ -16,7 +16,7 @@ if "bpy" in locals():
|
||||
|
||||
import bpy
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
from mathutils import Matrix, Euler, Vector
|
||||
from mathutils import Matrix, Euler, Vector, Quaternion
|
||||
|
||||
# Also imported in .fbx_utils, so importing here is unlikely to further affect Blender startup time.
|
||||
import numpy as np
|
||||
@ -527,8 +527,16 @@ def blen_read_object_transform_preprocess(fbx_props, fbx_obj, rot_alt_mat, use_p
|
||||
# ---------
|
||||
# Animation
|
||||
def _transformation_curves_gen(item, values_arrays, channel_keys):
|
||||
"""Yields flattened location/rotation/scaling values for imported PoseBone/Object Lcl Translation/Rotation/Scaling
|
||||
animation curve values.
|
||||
|
||||
The value arrays must have the same lengths where each index of each array corresponds to a single keyframe.
|
||||
|
||||
Each value array must have a corresponding channel key tuple that identifies the fbx property
|
||||
(b'Lcl Translation'/b'Lcl Rotation'/b'Lcl Scaling') and the channel (x/y/z as 0/1/2) of that property."""
|
||||
from operator import setitem
|
||||
from functools import partial
|
||||
|
||||
if item.is_bone:
|
||||
bl_obj = item.bl_obj.pose.bones[item.bl_bone]
|
||||
else:
|
||||
@ -548,6 +556,11 @@ def _transformation_curves_gen(item, values_arrays, channel_keys):
|
||||
b'Lcl Scaling': transform_data.sca,
|
||||
}
|
||||
|
||||
# Create a setter into transform_data for each values array. e.g. a values array for 'Lcl Scaling' with channel == 2
|
||||
# would set transform_data.sca[2].
|
||||
|
||||
setters = [partial(setitem, transform_prop_to_attr[fbx_prop], channel) for fbx_prop, channel in channel_keys]
|
||||
frame_values_it = zip(*(iter(arr.data) for arr in values_arrays))
|
||||
|
||||
# Pre-get/calculate these to reduce the work done inside the hot loop.
|
||||
anim_compensation_matrix = item.anim_compensation_matrix
|
||||
do_anim_compensation_matrix = bool(anim_compensation_matrix)
|
||||
@ -560,24 +573,17 @@ def _transformation_curves_gen(item, values_arrays, channel_keys):
|
||||
|
||||
do_restmat_inv = bool(restmat_inv)
|
||||
|
||||
# Create a setter into transform_data for each values array. e.g. a values array for 'Lcl Scaling' with channel == 2
|
||||
# would set transform_data.sca[2].
|
||||
# TODO: Might be faster to create a list of each transform_prop_to_attr[fbx_prop] and a list of channels, then zip
|
||||
# both and in the main loop, do transform_data_attr[channel] = value
|
||||
setters = [partial(setitem, transform_prop_to_attr[fbx_prop], channel) for fbx_prop, channel in channel_keys]
|
||||
zipped_values_iterators = zip(*(iter(arr.data) for arr in values_arrays))
|
||||
decompose = Matrix.decompose
|
||||
to_axis_angle = Quaternion.to_axis_angle
|
||||
to_euler = Quaternion.to_euler
|
||||
|
||||
# todo: Rather than having to get the Matrix/Quaternion methods upon each call within the loop, we can instead get
|
||||
# them in advance.
|
||||
# Before the loop:
|
||||
# `mat_decompose = Matrix.decompose`
|
||||
# then within the loop:
|
||||
# `mat_decompose(mat)`
|
||||
|
||||
for values in zipped_values_iterators:
|
||||
for setter, value in zip(setters, values):
|
||||
# Iterate through the values for each frame.
|
||||
for frame_values in frame_values_it:
|
||||
# Set each value into its corresponding attribute in transform_data.
|
||||
for setter, value in zip(setters, frame_values):
|
||||
setter(value)
|
||||
|
||||
# Calculate the updated matrix for this frame.
|
||||
mat, _, _ = blen_read_object_transform_do(transform_data)
|
||||
|
||||
# compensate for changes in the local matrix during processing
|
||||
@ -597,16 +603,16 @@ def _transformation_curves_gen(item, values_arrays, channel_keys):
|
||||
mat = restmat_inv @ mat
|
||||
|
||||
# Now we have a virtual matrix of transform from AnimCurves, we can insert keyframes!
|
||||
loc, rot, sca = mat.decompose()
|
||||
loc, rot, sca = decompose(mat)
|
||||
if rot_mode == 'QUATERNION':
|
||||
if rot_quat_prev.dot(rot) < 0.0:
|
||||
rot = -rot
|
||||
rot_quat_prev = rot
|
||||
elif rot_mode == 'AXIS_ANGLE':
|
||||
vec, ang = rot.to_axis_angle()
|
||||
vec, ang = to_axis_angle(rot)
|
||||
rot = ang, vec.x, vec.y, vec.z
|
||||
else: # Euler
|
||||
rot = rot.to_euler(rot_mode, rot_eul_prev)
|
||||
rot = to_euler(rot, rot_mode, rot_eul_prev)
|
||||
rot_eul_prev = rot
|
||||
|
||||
# Yield order matches the order that the location/rotation/scale FCurves are created in.
|
||||
|
Loading…
Reference in New Issue
Block a user
transform_data.scale[2]
I believe?In this case
.sca
is correct, theFBXTransformData
namedtuple uses rather short attribute names.