FBX IO: Speed up animation simplification using NumPy #104904
@ -1314,42 +1314,88 @@ 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...
|
||||
|
||||
are_keyed = []
|
||||
for values, frame_write_mask in zip(self._frame_values_array, self._frame_write_mask_array):
|
||||
# Initialise to no frames written.
|
||||
frame_write_mask[:] = False
|
||||
|
||||
# Create views of the 'previous' and 'current' mask and values. The memoryview, .data, of each array is used
|
||||
# for its iteration and indexing performance compared to the array.
|
||||
key = values[1:].data
|
||||
p_key = values[:-1].data
|
||||
key_write = frame_write_mask[1:].data
|
||||
p_key_write = frame_write_mask[:-1].data
|
||||
abs_values = np.abs(values)
|
||||
|
||||
p_keyedval = values[0]
|
||||
is_keyed = False
|
||||
for idx, (val, p_val) in enumerate(zip(key, p_key)):
|
||||
if val == p_val:
|
||||
# Never write keyframe when value is exactly the same as prev one!
|
||||
continue
|
||||
# This is contracted form of relative + absolute-near-zero difference:
|
||||
# absdiff = abs(a - b)
|
||||
# if absdiff < min_reldiff_fac * min_absdiff_fac:
|
||||
# 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.
|
||||
if abs(val - p_val) > (min_reldiff_fac * max(abs(val) + abs(p_val), min_absdiff_fac)):
|
||||
# If enough difference from previous sampled value, key this value *and* the previous one!
|
||||
key_write[idx] = True
|
||||
p_key_write[idx] = True
|
||||
p_keyedval = val
|
||||
is_keyed = True
|
||||
elif abs(val - p_keyedval) > (min_reldiff_fac * max((abs(val) + abs(p_keyedval)), min_absdiff_fac)):
|
||||
# Else, if enough difference from previous keyed value, key this value only!
|
||||
key_write[idx] = True
|
||||
p_keyedval = val
|
||||
is_keyed = True
|
||||
are_keyed.append(is_keyed)
|
||||
# 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)
|
||||
# if absdiff < min_reldiff_fac * min_absdiff_fac:
|
||||
# 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_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_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 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]
|
||||
abs_val_check = abs_val[val_check_idx]
|
||||
|
||||
# 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.
|
||||
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 the relative + absolute-near-zero difference again, but against the previous keyed value this
|
||||
# time.
|
||||
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_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
|
||||
# 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 = 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 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.
|
||||
@ -1358,7 +1404,9 @@ class AnimationCurveNodeWrapper:
|
||||
# one key in this case.
|
||||
# See T41719, T41605, T41254...
|
||||
if self.force_keying or (force_keep and not self):
|
||||
are_keyed[:] = [True] * len(are_keyed)
|
||||
are_keyed = [True] * len(self._frame_write_mask_array)
|
||||
else:
|
||||
are_keyed = np.any(self._frame_write_mask_array, axis=1)
|
||||
|
||||
# If we did key something, ensure first and last sampled values are keyed as well.
|
||||
if self.force_startend_keying:
|
||||
|
Loading…
Reference in New Issue
Block a user