FBX IO: Speed up animation import using NumPy #104856
@ -621,33 +621,35 @@ def _transformation_curves_gen(item, values_arrays, channel_keys):
|
||||
yield from sca
|
||||
|
||||
|
||||
def _combine_same_property_curves(times_and_values_tuples):
|
||||
"""Combine multiple sorted animation curves, that affect the same property, into a single sorted animation curve."""
|
||||
if len(times_and_values_tuples) > 1:
|
||||
# TODO: Print a warning to the console that more than one curve was found
|
||||
def blen_read_animation_channel_curves(curves):
|
||||
"""Read one or (rarely) more animation curves, that affect the same channel of the same property, from FBX data.
|
||||
|
||||
When there are multiple curves, they will be combined into a single sorted animation curve.
|
||||
|
||||
Though, it is expected that there will almost never be more than a single curve to read because multiple curves
|
||||
affecting the same channel of the same property is not part of FBX's default animation system.
|
||||
|
||||
Returns an array of sorted, unique FBX keyframe times and an array of values for each of those keyframe times."""
|
||||
if len(curves) > 1:
|
||||
times_and_values_tuples = list(map(blen_read_single_animation_curve, curves))
|
||||
# The FBX animation system's default implementation only uses the first curve assigned to a channel.
|
||||
# Additional curves per channel are allowed by the FBX specification, but the handling of these curves is
|
||||
# considered the responsibility of the application that created them. Note that each curve node is expected to
|
||||
# have a unique set of channels, so these additional curves with the same channel would have to belong to
|
||||
# separate curve nodes. See the FBX SDK documentation for FbxAnimCurveNode.
|
||||
|
||||
# Concatenate all the times into one array and all the values into one array.
|
||||
all_times = np.concatenate([t[0] for t in times_and_values_tuples])
|
||||
all_values = np.concatenate([t[1] for t in times_and_values_tuples])
|
||||
# Combine the curves together to produce a single array of sorted keyframe times and a single array of values.
|
||||
# The arrays are concatenated in reverse so that if there are duplicate times in the read curves, then only the
|
||||
# value of the last occurrence is kept.
|
||||
all_times = np.concatenate([t[0] for t in reversed(times_and_values_tuples)])
|
||||
all_values = np.concatenate([t[1] for t in reversed(times_and_values_tuples)])
|
||||
# Get the unique, sorted times and the index in all_times of the first occurrence of each unique value.
|
||||
sorted_unique_times, unique_indices_in_all_times = np.unique(all_times, return_index=True)
|
||||
|
||||
values_of_sorted_unique_times = all_values[unique_indices_in_all_times]
|
||||
return sorted_unique_times, values_of_sorted_unique_times
|
||||
|
||||
# # Get the indices that would sort all_times.
|
||||
# # Use a stable algorithm so that if there are any duplicate times, they maintain their original order.
|
||||
# perm = np.argsort(kind='stable')
|
||||
# # Use the indices to sort both all_times and all_values.
|
||||
# all_times = all_times[perm]
|
||||
# all_values = all_values[perm]
|
||||
else:
|
||||
return times_and_values_tuples[0]
|
||||
return blen_read_single_animation_curve(curves[0])
|
||||
|
||||
|
||||
def _combine_curve_keyframes(times_and_values_tuples, initial_values):
|
||||
@ -888,10 +890,7 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset, glo
|
||||
for channel, curves in channel_to_curves.items():
|
||||
assert(channel in {0, 1, 2})
|
||||
blen_curve = blen_curves[channel]
|
||||
|
||||
parsed_curves = tuple(map(blen_read_single_animation_curve, curves))
|
||||
fbx_key_times, values = _combine_same_property_curves(parsed_curves)
|
||||
|
||||
fbx_key_times, values = blen_read_animation_channel_curves(curves)
|
||||
blen_store_keyframes(fbx_key_times, blen_curve, values, anim_offset, fps)
|
||||
|
||||
elif isinstance(item, ShapeKey):
|
||||
@ -902,12 +901,11 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset, glo
|
||||
assert(channel == 0)
|
||||
blen_curve = blen_curves[channel]
|
||||
|
||||
parsed_curves = tuple(map(blen_read_single_animation_curve, curves))
|
||||
fbx_key_times, values = _combine_same_property_curves(parsed_curves)
|
||||
fbx_key_times, values = blen_read_animation_channel_curves(curves)
|
||||
# A fully activated shape key in FBX DeformPercent is 100.0 whereas it is 1.0 in Blender.
|
||||
values = values / 100.0
|
||||
|
||||
blen_store_keyframes(fbx_key_times, blen_curve, values, anim_offset, fps)
|
||||
|
||||
# Store the minimum and maximum shape key values, so that the shape key's slider range can be expanded if
|
||||
# necessary after reading all animations.
|
||||
deform_values.append(values.min())
|
||||
@ -922,8 +920,7 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset, glo
|
||||
# The indices are determined by the creation of the `props` list above.
|
||||
blen_curve = blen_curves[1 if is_focus_distance else 0]
|
||||
|
||||
parsed_curves = tuple(map(blen_read_single_animation_curve, curves))
|
||||
fbx_key_times, values = _combine_same_property_curves(parsed_curves)
|
||||
fbx_key_times, values = blen_read_animation_channel_curves(curves)
|
||||
if is_focus_distance:
|
||||
# Remap the imported values from FBX to Blender.
|
||||
values = values / 1000.0
|
||||
@ -950,8 +947,7 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset, glo
|
||||
continue
|
||||
for channel, curves in channel_to_curves.items():
|
||||
assert(channel in {0, 1, 2})
|
||||
parsed_curves = tuple(map(blen_read_single_animation_curve, curves))
|
||||
fbx_key_times, values = _combine_same_property_curves(parsed_curves)
|
||||
fbx_key_times, values = blen_read_animation_channel_curves(curves)
|
||||
|
||||
channel_keys.append((fbxprop, channel))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user