This commit is contained in:
2026-04-07 17:38:53 -07:00
parent ef1fa0b6bd
commit 019f5f76a2
40 changed files with 281 additions and 0 deletions

BIN
0001.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

BIN
0002.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

BIN
0003.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

BIN
0004.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

BIN
0005.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 719 KiB

BIN
0006.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

BIN
0007.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

BIN
0008.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 KiB

BIN
0009.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 KiB

BIN
0010.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 KiB

BIN
0011.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 KiB

BIN
0012.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

BIN
0013.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

BIN
0014.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 720 KiB

BIN
0015.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 KiB

BIN
0016.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

BIN
0017.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

BIN
0018.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 KiB

BIN
0019.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 KiB

BIN
0020.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 KiB

BIN
0021.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

BIN
0022.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

BIN
0023.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

BIN
0024.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 KiB

BIN
0025.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 727 KiB

BIN
0026.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

BIN
0027.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 KiB

BIN
0028.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

BIN
0029.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

BIN
0030.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

281
mixamo_openpose.py Normal file
View File

@@ -0,0 +1,281 @@
bl_info = {
"name": "OpenPose Mixamo Connect",
"author": "Your Name",
"version": (1, 0, 0),
"blender": (3, 0, 0),
"location": "View3D > Sidebar > OpenPose",
"description": "Connect OpenPose rig to Mixamo armature with bone mapping",
"warning": "",
"doc_url": "",
"category": "Rigging",
}
import bpy
import os
from bpy.types import Panel, Operator, PropertyGroup
from bpy.props import PointerProperty, StringProperty
# Bone mapping dictionary
BONE_MAP = {
'Head': 'mixamorig:Head',
'Foot_R': 'mixamorig:RightFoot',
'Leg_R': 'mixamorig:RightLeg',
'Foot_L': 'mixamorig:LeftFoot',
'Leg_L': 'mixamorig:LeftLeg',
'Pelvis_R': 'mixamorig:RightUpLeg',
'Pelvis_L': 'mixamorig:LeftUpLeg',
'UpperArm_R': 'mixamorig:RightArm',
'UpperArm_L': 'mixamorig:LeftArm',
'ForeArm_R': 'mixamorig:RightForeArm',
'ForeArm_L': 'mixamorig:LeftForeArm',
'Hand_R': 'mixamorig:RightHand',
'Hand_L': 'mixamorig:LeftHand',
'Bone.007': 'mixamorig:Spine2',
}
class OPENPOSE_OT_add_rig(Operator):
"""Add OpenPose rig to the scene from bundled blend file"""
bl_idname = "openpose.add_rig"
bl_label = "Add OpenPose Rig"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
# Get the addon directory
addon_dir = os.path.dirname(os.path.realpath(__file__))
blend_file = os.path.join(addon_dir, "openpose.blend")
blend_file = "/home/noti/dev/blender_mixamo_openpose_attach/openpose.blend"
# Check if the blend file exists
if not os.path.exists(blend_file):
self.report({'ERROR'}, f"OpenPose rig file not found: {blend_file}")
return {'CANCELLED'}
# Try to append the OpenPose rig
try:
# Get all existing objects before appending
existing_objects = set(bpy.data.objects)
# Append all objects from the blend file
with bpy.data.libraries.load(blend_file, link=False) as (data_from, data_to):
data_to.objects = data_from.objects
# Add the appended objects to the current scene
new_objects = []
for obj in data_to.objects:
if obj is not None and obj not in existing_objects:
context.collection.objects.link(obj)
new_objects.append(obj)
# Select the newly added objects
bpy.ops.object.select_all(action='DESELECT')
for obj in new_objects:
obj.select_set(True)
if obj.type == 'ARMATURE':
context.view_layer.objects.active = obj
self.report({'INFO'}, f"Added OpenPose rig: {len(new_objects)} object(s)")
return {'FINISHED'}
except Exception as e:
self.report({'ERROR'}, f"Failed to load OpenPose rig: {str(e)}")
return {'CANCELLED'}
class OPENPOSE_OT_connect_rigs(Operator):
"""Connect OpenPose rig to Mixamo armature using bone constraints"""
bl_idname = "openpose.connect_rigs"
bl_label = "Connect Rigs"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
props = context.scene.openpose_props
return (props.source_armature is not None and
props.target_armature is not None and
props.source_armature.type == 'ARMATURE' and
props.target_armature.type == 'ARMATURE')
def execute(self, context):
props = context.scene.openpose_props
source_armature = props.source_armature
target_armature = props.target_armature
if source_armature == target_armature:
self.report({'ERROR'}, "Source and target armatures must be different")
return {'CANCELLED'}
# Statistics
mapped_count = 0
missing_source = []
missing_target = []
# Loop through the bone map
for source_bone_name, target_bone_name in BONE_MAP.items():
# Get the pose bones
source_bone = source_armature.pose.bones.get(source_bone_name)
target_bone = target_armature.pose.bones.get(target_bone_name)
if not source_bone:
missing_source.append(source_bone_name)
continue
if not target_bone:
missing_target.append(target_bone_name)
continue
# Clear existing Copy Location constraints from source bone
for constraint in list(source_bone.constraints):
if constraint.type == 'COPY_LOCATION':
source_bone.constraints.remove(constraint)
# Add Copy Location constraint
cl_constraint = source_bone.constraints.new('COPY_LOCATION')
cl_constraint.target = target_armature
cl_constraint.subtarget = target_bone_name
cl_constraint.use_offset = False
mapped_count += 1
# Update scene
context.view_layer.update()
# Report results
report_msg = f"Connected {mapped_count} bones"
if missing_source:
report_msg += f" | Missing in source: {len(missing_source)}"
if missing_target:
report_msg += f" | Missing in target: {len(missing_target)}"
self.report({'INFO'}, report_msg)
if missing_source or missing_target:
print(f"\n=== OpenPose Mixamo Connect Report ===")
if missing_source:
print(f"Missing source bones: {', '.join(missing_source)}")
if missing_target:
print(f"Missing target bones: {', '.join(missing_target)}")
return {'FINISHED'}
class OPENPOSE_OT_clear_constraints(Operator):
"""Clear all Copy Location constraints from the source armature"""
bl_idname = "openpose.clear_constraints"
bl_label = "Clear Constraints"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
props = context.scene.openpose_props
return props.source_armature is not None and props.source_armature.type == 'ARMATURE'
def execute(self, context):
props = context.scene.openpose_props
source_armature = props.source_armature
cleared_count = 0
for bone in source_armature.pose.bones:
for constraint in list(bone.constraints):
if constraint.type == 'COPY_LOCATION':
bone.constraints.remove(constraint)
cleared_count += 1
context.view_layer.update()
self.report({'INFO'}, f"Cleared {cleared_count} constraint(s)")
return {'FINISHED'}
class OPENPOSE_PT_main_panel(Panel):
"""Main panel for OpenPose Mixamo Connect addon"""
bl_label = "OpenPose Mixamo Connect"
bl_idname = "OPENPOSE_PT_main_panel"
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = 'OpenPose'
def draw(self, context):
layout = self.layout
props = context.scene.openpose_props
# Add OpenPose Rig section
box = layout.box()
box.label(text="1. Add OpenPose Rig", icon='ARMATURE_DATA')
box.operator("openpose.add_rig", icon='ADD')
layout.separator()
# Select Armatures section
box = layout.box()
box.label(text="2. Select Armatures", icon='OBJECT_DATA')
col = box.column(align=True)
col.label(text="Source (OpenPose):")
col.prop(props, "source_armature", text="")
col.separator()
col.label(text="Target (Mixamo):")
col.prop(props, "target_armature", text="")
layout.separator()
# Connect Rigs section
box = layout.box()
box.label(text="3. Connect Rigs", icon='LINKED')
col = box.column(align=True)
col.operator("openpose.connect_rigs", icon='CON_LOCLIKE')
col.operator("openpose.clear_constraints", icon='X')
# Info section
if props.source_armature and props.target_armature:
layout.separator()
info_box = layout.box()
info_box.label(text=f"Bone mappings: {len(BONE_MAP)}", icon='INFO')
class OpenPoseProperties(PropertyGroup):
"""Property group to store addon properties"""
source_armature: PointerProperty(
name="Source Armature",
description="The OpenPose armature (will receive constraints)",
type=bpy.types.Object,
poll=lambda self, obj: obj.type == 'ARMATURE'
)
target_armature: PointerProperty(
name="Target Armature",
description="The Mixamo armature (constraint target)",
type=bpy.types.Object,
poll=lambda self, obj: obj.type == 'ARMATURE'
)
# Registration
classes = (
OpenPoseProperties,
OPENPOSE_OT_add_rig,
OPENPOSE_OT_connect_rigs,
OPENPOSE_OT_clear_constraints,
OPENPOSE_PT_main_panel,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
bpy.types.Scene.openpose_props = PointerProperty(type=OpenPoseProperties)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)
del bpy.types.Scene.openpose_props
if __name__ == "__main__":
register()

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
my_animation_se_0025.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
my_animation_se_0029.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
openpose.blend Normal file

Binary file not shown.

BIN
openpose.blend1 Normal file

Binary file not shown.

BIN
openpose.blend2 Normal file

Binary file not shown.

BIN
openpose.blend3 Normal file

Binary file not shown.

BIN
openpose.blend4 Normal file

Binary file not shown.