81 lines
2.6 KiB
Python
81 lines
2.6 KiB
Python
import bpy
|
||
import math
|
||
from mathutils import Vector
|
||
|
||
def redistribute_keyframes(obj, loc_factor=1.0, rot_factor=2.0):
|
||
"""Redistribute keyframes of an object based on motion magnitude,
|
||
with square-root damping to avoid extreme slowdowns when both
|
||
translation and rotation are large."""
|
||
|
||
anim = obj.animation_data
|
||
if not anim or not anim.action:
|
||
print("No animation data found")
|
||
return
|
||
|
||
action = anim.action
|
||
|
||
# Collect unique keyframe times
|
||
keyframe_times = sorted(set(
|
||
int(kp.co[0]) for fc in action.fcurves for kp in fc.keyframe_points
|
||
))
|
||
|
||
if len(keyframe_times) < 2:
|
||
print("Not enough keyframes")
|
||
return
|
||
|
||
magnitudes = []
|
||
for i in range(1, len(keyframe_times)):
|
||
f0 = keyframe_times[i-1]
|
||
f1 = keyframe_times[i]
|
||
|
||
bpy.context.scene.frame_set(f0)
|
||
loc0 = obj.location.copy()
|
||
rot0 = obj.rotation_euler.to_quaternion()
|
||
|
||
bpy.context.scene.frame_set(f1)
|
||
loc1 = obj.location.copy()
|
||
rot1 = obj.rotation_euler.to_quaternion()
|
||
|
||
d_loc = (loc1 - loc0).length
|
||
d_rot = rot0.rotation_difference(rot1).angle
|
||
|
||
# 💡 Square-root damping here
|
||
magnitude = math.sqrt((d_loc * loc_factor)**2 + (d_rot * rot_factor)**2)
|
||
magnitudes.append(magnitude)
|
||
|
||
total_magnitude = sum(magnitudes)
|
||
if total_magnitude == 0:
|
||
print("No motion detected between keyframes.")
|
||
return
|
||
|
||
# Use timeline range
|
||
frame_start = bpy.context.scene.frame_start
|
||
frame_end = bpy.context.scene.frame_end
|
||
available_range = frame_end - frame_start
|
||
|
||
proportions = [m / total_magnitude for m in magnitudes]
|
||
frame_offsets = [p * available_range for p in proportions]
|
||
|
||
# New keyframe positions
|
||
new_times = [frame_start]
|
||
for offset in frame_offsets:
|
||
new_times.append(new_times[-1] + offset)
|
||
|
||
# Reposition keyframes
|
||
for fc in action.fcurves:
|
||
for kp in fc.keyframe_points:
|
||
original_frame = int(kp.co[0])
|
||
if original_frame in keyframe_times:
|
||
idx = keyframe_times.index(original_frame)
|
||
new_frame = new_times[idx]
|
||
kp.co[0] = new_frame
|
||
kp.handle_left[0] = new_frame
|
||
kp.handle_right[0] = new_frame
|
||
fc.update()
|
||
|
||
bpy.context.scene.frame_set(frame_start)
|
||
print(f"Keyframes redistributed with sqrt damping (loc_factor={loc_factor}, rot_factor={rot_factor}) "
|
||
f"over timeline {frame_start}–{frame_end}.")
|
||
|
||
# Example: run on active object with rotation emphasized
|
||
redistribute_keyframes(bpy.context.active_object, loc_factor=1.0, rot_factor=10)
|