Utility_Apps/Blender/simple scripts/Redistributes keyframes by motion/Redistributes keyframes by motion.py

81 lines
2.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)