## Description
The script merges all selected armatures in Blender into the active armature, preserving the bone hierarchy and relevant bone properties. If the active armature already contains bones with the same names as those in the selected armatures, those bones will not be duplicated. Instead, only new bones will be added to the active armature. Additionally, child objects of the selected armatures will be transferred to the active armature. The script ensures that the transforms (position, rotation, and scale) of the bones and child objects relative to the world space remain unchanged during the merge process.
## How to Use
1. Open Blender and navigate to the Scripting workspace or Text Editor.
2. Create a new text file and paste the script into the Text Editor.
3. In the 3D viewport, select the armatures you want to merge. Make sure the active object (highlighted in a lighter color) is the armature you want to merge the selected armatures into.
4. Go back to the Scripting workspace or Text Editor and click the “Run Script” button to execute the script.
5. The selected armatures will be merged into the active armature, and their bones and child objects will be transferred accordingly. The selected armatures will be deleted from the scene.
Note: Before using the script, please ensure you have a backup of your work to avoid potential data loss. The script is intended to be used in Blender’s Object mode.
## Source
```python
import bpy
import mathutils
# Get the active object and selected objects
active_obj = bpy.context.active_object
selected_objs = bpy.context.selected_objects
# Ensure the active object is an armature
if not active_obj.type == 'ARMATURE':
raise Exception("Active object must be an armature.")
# Get the armature data of the active object
active_armature = active_obj.data
# Store the original mode and switch to Edit mode
original_mode = bpy.context.mode
bpy.ops.object.mode_set(mode='EDIT')
# Iterate over selected armatures and merge bones
for obj in selected_objs:
if obj == active_obj or obj.type != 'ARMATURE':
continue
# Copy bones without setting parent-child relationship
for bone in obj.data.bones:
bone_matrix = bone.matrix_local
if bone.name not in active_armature.edit_bones:
new_bone = active_armature.edit_bones.new(bone.name)
new_bone.head = active_obj.matrix_world.inverted() @ obj.matrix_world @ bone.head_local
new_bone.tail = active_obj.matrix_world.inverted() @ obj.matrix_world @ bone.tail_local
new_bone.use_connect = bone.use_connect
# Set parent-child relationship for copied bones
for bone in obj.data.bones:
if bone.name in active_armature.edit_bones and bone.parent is not None:
new_bone = active_armature.edit_bones[bone.name]
new_bone.parent = active_armature.edit_bones.get(bone.parent.name)
# Change mode to Object mode to update child objects
bpy.ops.object.mode_set(mode='OBJECT')
# Copy children and update armature references in modifiers
for child in obj.children:
# Store original world matrix of the child
original_matrix_world = child.matrix_world.copy()
# Update parent and restore original world matrix
child.parent = active_obj
child.matrix_world = original_matrix_world
# Update armature references in modifiers
for modifier in child.modifiers:
if modifier.type == 'ARMATURE' and modifier.object == obj:
modifier.object = active_obj
# Delete the armature after merging its bones
bpy.data.objects.remove(obj, do_unlink=True)
# Change mode back to Edit mode
bpy.ops.object.mode_set(mode='EDIT')
# Update the scene and switch back to the original mode
bpy.context.view_layer.update()
bpy.ops.object.mode_set(mode=original_mode)
```