FBX IO: Speed up animation simplification using NumPy #104904
@ -2255,6 +2255,8 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
|
||||
has_animated_duplis = bool(dupli_parent_bdata)
|
||||
|
||||
# Initialize keyframe times array. Each AnimationCurveNodeWrapper will share the same instance.
|
||||
# `np.arange` excludes the `stop` argument like when using `range`, so we use np.nextafter to get the next
|
||||
# representable value after f_end and use that as the `stop` argument instead.
|
||||
currframes = np.arange(f_start, np.nextafter(f_end, np.inf), step=bake_step)
|
||||
real_currframes = currframes - f_start if start_zero else currframes
|
||||
|
||||
|
@ -1283,9 +1283,9 @@ class AnimationCurveNodeWrapper:
|
||||
def set_keyframes(self, keyframe_times, keyframe_values):
|
||||
"""
|
||||
Set all keyframe times and values of the group.
|
||||
Values can be a 2D array where each row is a separate curve.
|
||||
Values can be a 2D array where each row is the values for a separate curve.
|
||||
"""
|
||||
# View 1D keyframe_values as 2D with a single row, so that the same iterative code can be used for both 1D and
|
||||
# View 1D keyframe_values as 2D with a single row, so that the same code can be used for both 1D and
|
||||
# 2D inputs.
|
||||
if len(keyframe_values.shape) == 1:
|
||||
keyframe_values = keyframe_values[np.newaxis]
|
||||
@ -1304,6 +1304,7 @@ class AnimationCurveNodeWrapper:
|
||||
* their values relatively differ from the previous sample ones.
|
||||
"""
|
||||
if self._frame_times_array is None:
|
||||
# Keyframes have not been added yet.
|
||||
return
|
||||
|
||||
if fac == 0.0:
|
||||
@ -1313,18 +1314,19 @@ class AnimationCurveNodeWrapper:
|
||||
min_reldiff_fac = fac * 1.0e-3 # min relative value evolution: 0.1% of current 'order of magnitude'.
|
||||
min_absdiff_fac = 0.1 # A tenth of reldiff...
|
||||
|
||||
for values, write_mask in zip(self._frame_values_array, self._frame_write_mask_array):
|
||||
for values, frame_write_mask in zip(self._frame_values_array, self._frame_write_mask_array):
|
||||
# Initialise to no frames written.
|
||||
write_mask[:] = False
|
||||
frame_write_mask[:] = False
|
||||
|
||||
# Create views of the 'previous' and 'current'
|
||||
p_key_write_mask = write_mask[:-1]
|
||||
key_write_mask = write_mask[1:]
|
||||
p_val = values[:-1]
|
||||
val = values[1:]
|
||||
abs_values = np.abs(values)
|
||||
p_val_abs = abs_values[:-1]
|
||||
val_abs = abs_values[1:]
|
||||
|
||||
# Create views of the 'previous' and 'current' mask and values.
|
||||
p_val = values[:-1]
|
||||
abs_p_val = abs_values[:-1]
|
||||
p_write_mask = frame_write_mask[:-1]
|
||||
val = values[1:]
|
||||
abs_val = abs_values[1:]
|
||||
write_mask = frame_write_mask[1:]
|
||||
|
||||
# This is contracted form of relative + absolute-near-zero difference:
|
||||
# absdiff = abs(a - b)
|
||||
@ -1332,67 +1334,68 @@ class AnimationCurveNodeWrapper:
|
||||
# return False
|
||||
# return (absdiff / ((abs(a) + abs(b)) / 2)) > min_reldiff_fac
|
||||
# Note that we ignore the '/ 2' part here, since it's not much significant for us.
|
||||
enough_diff_prev_sampled_mask = (
|
||||
np.abs(val - p_val) > (min_reldiff_fac * np.maximum(val_abs + p_val_abs, min_absdiff_fac))
|
||||
enough_diff_p_val_mask = (
|
||||
np.abs(val - p_val) > (min_reldiff_fac * np.maximum(abs_val + abs_p_val, min_absdiff_fac))
|
||||
)
|
||||
# If enough difference from previous sampled value, key this value *and* the previous one!
|
||||
# Unless it is forcefully keyed later, this is the only way that the first value can be keyed.
|
||||
p_key_write_mask[enough_diff_prev_sampled_mask] = True
|
||||
key_write_mask[enough_diff_prev_sampled_mask] = True
|
||||
p_write_mask[enough_diff_p_val_mask] = True
|
||||
write_mask[enough_diff_p_val_mask] = True
|
||||
|
||||
# The other case where we key a value is if there is enough difference between it and the previous keyed
|
||||
# value.
|
||||
# Values that equal their previous value are skipped and the remaining values to check are those which are
|
||||
# currently not keyed
|
||||
not_keyed_mask = ~key_write_mask
|
||||
# Values that equal their previous value can be skipped, so the remaining values to check are those which
|
||||
# are currently not keyed.
|
||||
not_keyed_mask = ~write_mask
|
||||
check_diff_mask = np.logical_and(not_keyed_mask, p_val != val)
|
||||
val_check_idx = np.flatnonzero(check_diff_mask)
|
||||
val_check = val[val_check_idx]
|
||||
val_abs_check = val_abs[val_check_idx]
|
||||
abs_val_check = abs_val[val_check_idx]
|
||||
|
||||
# For each frame, get the index of the previous keyed value.
|
||||
prev_keyframe_indices = np.arange(1, len(values))
|
||||
# The first 'previous keyframe' defaults to values[0], even if it's not actually keyed.
|
||||
prev_keyframe_indices[not_keyed_mask] = 0
|
||||
# Accumulative maximum fills in the zeroed indices with the closest previous non-zero index.
|
||||
prev_keyframe_indices = np.maximum.accumulate(prev_keyframe_indices)
|
||||
# For each current value, get the index of the previous keyed value in `values`.
|
||||
p_keyed_idx = np.arange(1, len(values))
|
||||
# The first 'previous keyed value' defaults to values[0], even if it's not actually keyed.
|
||||
p_keyed_idx[not_keyed_mask] = 0
|
||||
# Accumulative maximum fills in the zeroed indices with the closest previous non-zero index because the
|
||||
# indices must be increasing.
|
||||
p_keyed_idx = np.maximum.accumulate(p_keyed_idx)
|
||||
# Extract only the indices that need checking.
|
||||
prev_keyframe_indices = prev_keyframe_indices[val_check_idx]
|
||||
p_kf_vals = values[prev_keyframe_indices]
|
||||
p_kf_vals_abs = np.abs(p_kf_vals)
|
||||
p_keyed_idx_check = p_keyed_idx[val_check_idx]
|
||||
p_keyed_val_check = values[p_keyed_idx_check]
|
||||
abs_p_keyed_val_check = np.abs(p_keyed_val_check)
|
||||
|
||||
# We check our relative + absolute-near-zero difference again, but against the previous keyed value this
|
||||
# We check the relative + absolute-near-zero difference again, but against the previous keyed value this
|
||||
# time.
|
||||
enough_diff_prev_keyed_mask = (
|
||||
np.abs(val_check - p_kf_vals)
|
||||
> (min_reldiff_fac * np.maximum(val_abs_check + p_kf_vals_abs, min_absdiff_fac))
|
||||
enough_diff_p_keyed_val_mask = (
|
||||
np.abs(val_check - p_keyed_val_check)
|
||||
> (min_reldiff_fac * np.maximum(abs_val_check + abs_p_keyed_val_check, min_absdiff_fac))
|
||||
)
|
||||
if np.any(enough_diff_prev_keyed_mask):
|
||||
if np.any(enough_diff_p_keyed_val_mask):
|
||||
# If there are any that are different enough from the previous keyed value, then we have to check them
|
||||
# all iteratively because keying a new value can change the previous keyed value of some elements, which
|
||||
# can change whether a value is different enough from its previous keyed value.
|
||||
last_new_kf_idx = -1
|
||||
last_new_kf_val = -1
|
||||
last_new_kf_val_abs = -1
|
||||
# Accessing .data, the memoryview of the array iteratively or by individual index is faster than doing
|
||||
# changes their relative + absolute-near-zero difference.
|
||||
new_p_keyed_idx = -1
|
||||
new_p_keyed_val = -1
|
||||
new_p_keyed_val_abs = -1
|
||||
# Accessing .data, the memoryview of the array, iteratively or by individual index is faster than doing
|
||||
# the same with the array itself.
|
||||
key_write_mv = key_write_mask.data
|
||||
zipped = zip(val_check_idx.data, val_check.data, val_abs_check.data, prev_keyframe_indices.data,
|
||||
enough_diff_prev_keyed_mask.data)
|
||||
for cur_idx, cur_val, abs_cur_val, p_kf_idx, enough_diff in zipped:
|
||||
if last_new_kf_idx > p_kf_idx:
|
||||
# The previous keyframe is new and was not included when enough_diff_prev_keyed_mask was
|
||||
# calculated, so whether the current value is different enough from the previous keyframe needs
|
||||
# to be calculated.
|
||||
# Check if the relative + absolute-near-zero difference is enough to key this frame.
|
||||
enough_diff = (abs(cur_val - last_new_kf_val)
|
||||
> (min_reldiff_fac * max(abs_cur_val + last_new_kf_val_abs, min_absdiff_fac)))
|
||||
key_write_mv = write_mask.data
|
||||
zipped = zip(val_check_idx.data, val_check.data, abs_val_check.data, p_keyed_idx_check.data,
|
||||
enough_diff_p_keyed_val_mask.data)
|
||||
for cur_idx, cur_val, abs_cur_val, old_p_keyed_idx, enough_diff in zipped:
|
||||
if new_p_keyed_idx > old_p_keyed_idx:
|
||||
# The previous keyed value is new and was not included when enough_diff_p_keyed_val_mask was
|
||||
# calculated, so whether the current value is different enough needs to be calculated.
|
||||
# Check if the relative + absolute-near-zero difference is enough to key this value.
|
||||
enough_diff = (abs(cur_val - new_p_keyed_val)
|
||||
> (min_reldiff_fac * max(abs_cur_val + new_p_keyed_val_abs, min_absdiff_fac)))
|
||||
if enough_diff:
|
||||
# The current index needs to be keyed.
|
||||
last_new_kf_idx = cur_idx
|
||||
last_new_kf_val = cur_val
|
||||
last_new_kf_val_abs = abs_cur_val
|
||||
# The current value needs to be keyed.
|
||||
key_write_mv[cur_idx] = True
|
||||
# Update the index and values for this newly keyed value.
|
||||
new_p_keyed_idx = cur_idx
|
||||
new_p_keyed_val = cur_val
|
||||
new_p_keyed_val_abs = abs_cur_val
|
||||
|
||||
# If we write nothing (action doing nothing) and are in 'force_keep' mode, we key everything! :P
|
||||
# See T41766.
|
||||
@ -1407,10 +1410,10 @@ class AnimationCurveNodeWrapper:
|
||||
|
||||
# If we did key something, ensure first and last sampled values are keyed as well.
|
||||
if self.force_startend_keying:
|
||||
for is_keyed, write_mask in zip(are_keyed, self._frame_write_mask_array):
|
||||
for is_keyed, frame_write_mask in zip(are_keyed, self._frame_write_mask_array):
|
||||
if is_keyed:
|
||||
write_mask[:1] = True
|
||||
write_mask[-1:] = True
|
||||
frame_write_mask[:1] = True
|
||||
frame_write_mask[-1:] = True
|
||||
|
||||
def get_final_data(self, scene, ref_id, force_keep=False):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user