5e24e06796
After discovering that sub-packages in Blender add-ons don't work (this seems to be very broken!), I replaced the parsers subpackage with a parsers.py module (almost as simple as concatenating the source files in the package). I may be removing this name-parsing system from ABX entirely, soon, possibly moving it into KitCAT.
149 lines
6.3 KiB
Python
149 lines
6.3 KiB
Python
# copy_anim.py
|
|
"""
|
|
Blender Python code to copy animation between armatures or proxy armatures.
|
|
|
|
The purpose of the 'Copy Animation' feature is to allow for animation to be
|
|
copied from one armature to another, en masse, rather than having to individual
|
|
push and move action objects.
|
|
|
|
The main use for this is to repair files in which animated proxy rigs have
|
|
become incompatible or broken for some reason. Common examples include a name
|
|
change in the rig or armature object in a character asset file, extra bones
|
|
added, and so on. There is no simple way in Blender to update these proxies.
|
|
|
|
It is possible to create a new proxy, though, and with this tool to speed up
|
|
the process, the animation can be transferred to it all at once.
|
|
|
|
The tool also allows for the animation to be correctly copied and scaled by
|
|
a scale factor, so that animation can be copied from a proxy defined at one
|
|
scale to one defined at another.
|
|
|
|
This comes up when an animation file was built incorrectly at the wrong scale
|
|
and needs to be corrected, after animating has already begun.
|
|
|
|
The scaling feature has been tested on Rigify-based rigs, and resets the
|
|
bone constraints as needed, during the process.
|
|
"""
|
|
|
|
import bpy, bpy.types, bpy.utils, bpy.props
|
|
|
|
#----------------------------------------
|
|
## TOOLS
|
|
# This might be moved into another module later
|
|
|
|
def copy_object_animation(sourceObj, targetObjs,
|
|
dopesheet=False, nla=False, rescale=False, scale_factor=1.0,
|
|
report=print):
|
|
"""
|
|
Copy Dope Sheet & NLA editor animation from active object to selected objects.
|
|
Most useful with armatures. Assumes bones match. Can be rescaled in the process.
|
|
|
|
From StackExchange post:
|
|
https://blender.stackexchange.com/questions/74183/how-can-i-copy-nla-tracks-from-one-armature-to-another
|
|
"""
|
|
for targetObj in targetObjs:
|
|
if targetObj.animation_data is not None:
|
|
targetObj.animation_data_clear()
|
|
|
|
targetObj.animation_data_create()
|
|
|
|
source_animation_data = sourceObj.animation_data
|
|
target_animation_data = targetObj.animation_data
|
|
|
|
# copy the dopesheet animation (active animation)
|
|
if dopesheet:
|
|
report({'INFO'}, 'Copying Dopesheet animation')
|
|
if source_animation_data.action is None:
|
|
report({'WARNING'},
|
|
"CLEARING target dope sheet - old animation saved with 'fake user'")
|
|
if target_animation_data.action is not None:
|
|
target_animation_data.action.use_fake_user = True
|
|
target_animation_data.action = None
|
|
else:
|
|
if rescale:
|
|
target_animation_data.action = copy_animation_action_with_rescale(
|
|
source_animation_data.action, scale_factor)
|
|
else:
|
|
target_animation_data.action = copy_animation_action_with_rescale(
|
|
source_animation_data.action, scale_factor)
|
|
|
|
target_animation_data.action.name = targetObj.name + 'Action'
|
|
|
|
if nla:
|
|
report({'INFO'}, 'Copying NLA strips')
|
|
if source_animation_data:
|
|
# Create new NLA tracks based on the source
|
|
for source_nla_track in source_animation_data.nla_tracks:
|
|
target_nla_track = target_animation_data.nla_tracks.new()
|
|
target_nla_track.name = source_nla_track.name
|
|
# In each track, create action strips base on the source
|
|
for source_action_strip in source_nla_track.strips:
|
|
|
|
if rescale:
|
|
new_action = copy_animation_action_with_rescale(
|
|
source_action_strip.action, scale_factor)
|
|
else:
|
|
new_action = source_action_strip.action
|
|
|
|
target_action_strip = target_nla_track.strips.new(
|
|
new_action.name,
|
|
source_action_strip.frame_start,
|
|
new_action)
|
|
|
|
# For each strip, copy the properties -- EXCEPT the ones we
|
|
# need to protect or can't copy
|
|
# introspect property names (is there a better way to do this?)
|
|
props = [p for p in dir(source_action_strip) if
|
|
not p in ('action',)
|
|
and not p.startswith('__') and not p.startswith('bl_')
|
|
and source_action_strip.is_property_set(p)
|
|
and not source_action_strip.is_property_readonly(p)
|
|
and not source_action_strip.is_property_hidden(p)]
|
|
for prop in props:
|
|
setattr(target_action_strip, prop, getattr(source_action_strip, prop))
|
|
|
|
|
|
# Adapted from reference:
|
|
# https://www.reddit.com/r/blender/comments/eu3w6m/guide_how_to_scale_a_rigify_rig/
|
|
#
|
|
|
|
def reset_armature_stretch_constraints(rig_object):
|
|
"""
|
|
Reset stretch-to constraints on an armature object - necessary after rescaling.
|
|
"""
|
|
bone_count = 0
|
|
for bone in rig_object.pose.bones:
|
|
for constraint in bone.constraints:
|
|
if constraint.type == "STRETCH_TO":
|
|
constraint.rest_length = 0
|
|
bone_count += 1
|
|
return bone_count
|
|
|
|
|
|
def rescale_animation_action_in_place(action, scale_factor):
|
|
"""
|
|
Rescale a list of animation actions by a scale factor (in-place).
|
|
"""
|
|
#for fcurve in bpy.data.actions[action].fcurves:
|
|
for fcurve in action.fcurves:
|
|
data_path = fcurve.data_path
|
|
if data_path.startswith('pose.bones[') and data_path.endswith('].location'):
|
|
for p in fcurve.keyframe_points:
|
|
p.co[1] *= scale_factor
|
|
p.handle_left[1] *= scale_factor
|
|
p.handle_right[1] *= scale_factor
|
|
return action
|
|
|
|
def copy_animation_action_with_rescale(action, scale_factor):
|
|
"""
|
|
Copy an animation action, rescaled.
|
|
"""
|
|
new_action = action.copy()
|
|
new_action.name = new_action.name[:-4]+'.rescale'
|
|
return rescale_animation_action_in_place(new_action, scale_factor)
|
|
|
|
|
|
|
|
|
|
#----------------------------------------
|