3
$\begingroup$

I want to copy the NLA action strips from one armature to another armature. The simple approach does not work of course because the nla_tracks is read only :

tgt.animation_data.nla_tracks = src.animation_data.nla_tracks

So how would i do this?

sidenote: both armatures can use the same actions, so i expect that the NLA strips shoul dalso work on both armatures

$\endgroup$
3
  • $\begingroup$ Do you actually need to do this with python or are you expecting that is the only way? $\endgroup$
    – sambler
    Feb 22, 2017 at 1:31
  • $\begingroup$ I need to do this with Python as part of a rig update tool that we need to migrate "old rigs" to newer versions. $\endgroup$
    – Gaia Clary
    Feb 22, 2017 at 15:02
  • $\begingroup$ did my answer solved your problem? $\endgroup$
    – Tak
    Mar 4, 2017 at 10:59

4 Answers 4

7
$\begingroup$

Nice code Sri! Thanks! I just complete some missing parts at the end

import bpy

sourceObj = bpy.data.objects['Armature.001']
targetObj = bpy.data.objects['Armature']

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


if source_animation_data:
    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
        for source_action_strip in source_nla_track.strips:
            bla= target_nla_track.strips.new(
            source_action_strip.action.name,
            int(source_action_strip.frame_start),
            source_action_strip.action)
        
            bla.name = source_action_strip.name
            bla.frame_start = source_action_strip.frame_start
            bla.frame_end = source_action_strip.frame_end 
            bla.extrapolation = source_action_strip.extrapolation
            bla.blend_type = source_action_strip.blend_type
            bla.use_auto_blend = source_action_strip.use_auto_blend       
            bla.blend_in = source_action_strip.blend_in
            bla.blend_out= source_action_strip.blend_out            
            bla.mute = source_action_strip.mute
            bla.use_reverse = source_action_strip.use_reverse
        
            bla.action_frame_start = source_action_strip.action_frame_start
            bla.action_frame_end = source_action_strip.action_frame_end
            bla.scale = source_action_strip.scale
            bla.repeat = source_action_strip.repeat 
            bla.use_animated_influence = source_action_strip.use_animated_influence
            bla.influence = source_action_strip.influence            
            bla.use_animated_time = source_action_strip.use_animated_time
            bla.use_animated_time_cyclic = source_action_strip.use_animated_time_cyclic
            bla.strip_time = source_action_strip.strip_time
$\endgroup$
3
  • $\begingroup$ Worked like a charm. Thanks for sharing! $\endgroup$
    – JavaRunner
    Sep 26, 2021 at 8:14
  • $\begingroup$ Great script! Do you know how to extend it to copy also Transitions? $\endgroup$
    – Cenda
    May 12, 2022 at 1:50
  • $\begingroup$ @Cenda it looks to me like this script won't work without significant changes for transitions. It will break on the line that creates a new strip because transitions don't have actions so the new will fail. But there appears to be no way to create a new transition, so this script needs to skip the transition, add the next action, select it and the prior action and then call the op to add a transition. $\endgroup$ May 12, 2022 at 18:39
2
$\begingroup$

Here is Python for deep copy. Just replace the names of the source and target armatures. Hope it helps - Sri

import bpy

sourceObj = bpy.data.objects['SOURCE_ARMATURE']
targetObj = bpy.data.objects['TARGET_ARMATURE']

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


if source_animation_data:
    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
        for source_action_strip in source_nla_track.strips:
            target_nla_track.strips.new(source_action_strip.action.name, source_action_strip.frame_start, source_action_strip.action)
$\endgroup$
0
$\begingroup$

You can use the below script to create a new strip in the new armature and copy the strip you want from another armature to it.

import bpy

arm1 = bpy.data.objects['Armature']
arm2 = bpy.data.objects['Armature.001']

if arm2.animation_data is None:
    arm2.animation_data_create()

anim_data = arm2.animation_data
anim_data.use_nla = True

track = anim_data.nla_tracks.new()
offset = 0

strip_to_copy = arm1.animation_data.nla_tracks[0].strips[0]

track.strips.new(strip_to_copy.action.name, offset, strip_to_copy.action)

As shown below a new strip will be created for Armature.001 and the strip of Armature will be copied to it

enter image description here

The .blend file for this example can be found here:

$\endgroup$
2
  • $\begingroup$ This is at most a very shallow copy. Would need to look at track / strip properties as well. $\endgroup$
    – batFINGER
    Feb 22, 2017 at 8:33
  • $\begingroup$ @batFINGER I did my best to provide an answer with the knowledge I have, so I agree that I may be missing something, please feel free to update or edit the answer with any useful information you have to help the Gaia. Thank you very much. $\endgroup$
    – Tak
    Feb 22, 2017 at 8:49
0
$\begingroup$

There are four types of strips. The existing answers only handle tracks that consist of only action strips. This late answer is a bit of an improvement, because it handles the common case of transition strips. It is also more robust in that it doesn't fail if there are Meta or Sound strips. However it does not handle those strips either, merely passing on them.

import bpy
# https://blender.stackexchange.com/questions/263420/create-nla-transition-with-python
#
# Add a transition between two NLA strips
# Generate an override that can be used for this call.
win = bpy.context.window
scr = win.screen
areas  = [area for area in scr.areas if area.type == 'NLA_EDITOR']
regions   = [region for region in areas[0].regions if region.type == 'WINDOW']
override = { 'area': areas[0], 'region' : regions[0] }

# Replace this line with one that selects the object that has the NLA Tracks
object = bpy.context.active_object

# Replace this line with one that selects the NLA Track you want.
src_track = object.animation_data.nla_tracks['NlaTrack']

dst_track =  object.animation_data.nla_tracks.new()
dst_track.name = 'copy'

# from https://blender.stackexchange.com/a/163875/42221
# heavily adapted

def strip_fill_action(newstrip, strip):
    newstrip.name = strip.name
    newstrip.frame_start = strip.frame_start
    newstrip.frame_end = strip.frame_end 
    newstrip.extrapolation = strip.extrapolation
    newstrip.blend_type = strip.blend_type
    newstrip.use_auto_blend = strip.use_auto_blend       
    newstrip.blend_in = strip.blend_in
    newstrip.blend_out= strip.blend_out            
    newstrip.mute = strip.mute
    newstrip.use_reverse = strip.use_reverse
    newstrip.action_frame_start = strip.action_frame_start
    newstrip.action_frame_end = strip.action_frame_end
    newstrip.scale = strip.scale
    newstrip.repeat = strip.repeat 
    newstrip.use_animated_influence = strip.use_animated_influence
    newstrip.influence = strip.influence            
    newstrip.use_animated_time = strip.use_animated_time
    newstrip.use_animated_time_cyclic = strip.use_animated_time_cyclic
    newstrip.strip_time = strip.strip_time

# Copy the action (CLIP) strips, remembering where there are transitions.
transitions = list()
for index, strip in enumerate(src_track.strips):
    if strip.type == 'TRANSITION':
        transitions.append(index)
    elif strip.type == 'CLIP':
        newstrip = dst_track.strips.new(strip.name, 
                            int(strip.frame_start), strip.action)
        strip_fill_action(newstrip, strip)
    else:
        print(f"Unsupported type {strip.type} at {index}")

# Insert any transitions that were found.
for index in transitions:
    dst_track.strips[index-1].select = True
    dst_track.strips[index].select = True
    bpy.ops.nla.transition_add(override)
    dst_track.strips[index-1].select = False
    dst_track.strips[index].select = False

Note that it has the same deep copy as another answer. The deep copy was taken from an inspection of the manual, but the code is nearly identical, so the other answer deserves recognition as a source.

Also note that Blender updated Python to 3.10 for Blender 3.1, so floats are no longer automatically converted to ints.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .