diff --git a/0001.png b/0001.png new file mode 100644 index 0000000..6b38f8a Binary files /dev/null and b/0001.png differ diff --git a/0002.png b/0002.png new file mode 100644 index 0000000..f74509a Binary files /dev/null and b/0002.png differ diff --git a/0003.png b/0003.png new file mode 100644 index 0000000..1e7f591 Binary files /dev/null and b/0003.png differ diff --git a/0004.png b/0004.png new file mode 100644 index 0000000..133145a Binary files /dev/null and b/0004.png differ diff --git a/0005.png b/0005.png new file mode 100644 index 0000000..d66fadf Binary files /dev/null and b/0005.png differ diff --git a/0006.png b/0006.png new file mode 100644 index 0000000..d68ea26 Binary files /dev/null and b/0006.png differ diff --git a/0007.png b/0007.png new file mode 100644 index 0000000..95aca91 Binary files /dev/null and b/0007.png differ diff --git a/0008.png b/0008.png new file mode 100644 index 0000000..a7a3998 Binary files /dev/null and b/0008.png differ diff --git a/0009.png b/0009.png new file mode 100644 index 0000000..964eb5f Binary files /dev/null and b/0009.png differ diff --git a/0010.png b/0010.png new file mode 100644 index 0000000..c35df92 Binary files /dev/null and b/0010.png differ diff --git a/0011.png b/0011.png new file mode 100644 index 0000000..97c4af6 Binary files /dev/null and b/0011.png differ diff --git a/0012.png b/0012.png new file mode 100644 index 0000000..e9b1604 Binary files /dev/null and b/0012.png differ diff --git a/0013.png b/0013.png new file mode 100644 index 0000000..9433af9 Binary files /dev/null and b/0013.png differ diff --git a/0014.png b/0014.png new file mode 100644 index 0000000..589b58b Binary files /dev/null and b/0014.png differ diff --git a/0015.png b/0015.png new file mode 100644 index 0000000..73c9848 Binary files /dev/null and b/0015.png differ diff --git a/0016.png b/0016.png new file mode 100644 index 0000000..4bd5375 Binary files /dev/null and b/0016.png differ diff --git a/0017.png b/0017.png new file mode 100644 index 0000000..d1d2ada Binary files /dev/null and b/0017.png differ diff --git a/0018.png b/0018.png new file mode 100644 index 0000000..9ea1f2d Binary files /dev/null and b/0018.png differ diff --git a/0019.png b/0019.png new file mode 100644 index 0000000..b8094b8 Binary files /dev/null and b/0019.png differ diff --git a/0020.png b/0020.png new file mode 100644 index 0000000..fb36b01 Binary files /dev/null and b/0020.png differ diff --git a/0021.png b/0021.png new file mode 100644 index 0000000..f60a02c Binary files /dev/null and b/0021.png differ diff --git a/0022.png b/0022.png new file mode 100644 index 0000000..c32c0a7 Binary files /dev/null and b/0022.png differ diff --git a/0023.png b/0023.png new file mode 100644 index 0000000..1968ba0 Binary files /dev/null and b/0023.png differ diff --git a/0024.png b/0024.png new file mode 100644 index 0000000..8edb489 Binary files /dev/null and b/0024.png differ diff --git a/0025.png b/0025.png new file mode 100644 index 0000000..80491cd Binary files /dev/null and b/0025.png differ diff --git a/0026.png b/0026.png new file mode 100644 index 0000000..10fc27b Binary files /dev/null and b/0026.png differ diff --git a/0027.png b/0027.png new file mode 100644 index 0000000..b582d1a Binary files /dev/null and b/0027.png differ diff --git a/0028.png b/0028.png new file mode 100644 index 0000000..1d4025c Binary files /dev/null and b/0028.png differ diff --git a/0029.png b/0029.png new file mode 100644 index 0000000..62b6a4b Binary files /dev/null and b/0029.png differ diff --git a/0030.png b/0030.png new file mode 100644 index 0000000..d65dae3 Binary files /dev/null and b/0030.png differ diff --git a/mixamo_openpose.py b/mixamo_openpose.py new file mode 100644 index 0000000..5b7e352 --- /dev/null +++ b/mixamo_openpose.py @@ -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() diff --git a/my_animation_openpose_se_0024.png b/my_animation_openpose_se_0024.png new file mode 100644 index 0000000..80e0413 Binary files /dev/null and b/my_animation_openpose_se_0024.png differ diff --git a/my_animation_openpose_se_0025.png b/my_animation_openpose_se_0025.png new file mode 100644 index 0000000..32858e0 Binary files /dev/null and b/my_animation_openpose_se_0025.png differ diff --git a/my_animation_se_0025.png b/my_animation_se_0025.png new file mode 100644 index 0000000..aa13781 Binary files /dev/null and b/my_animation_se_0025.png differ diff --git a/my_animation_se_0029.png b/my_animation_se_0029.png new file mode 100644 index 0000000..bc41d9c Binary files /dev/null and b/my_animation_se_0029.png differ diff --git a/openpose.blend b/openpose.blend new file mode 100644 index 0000000..5722882 Binary files /dev/null and b/openpose.blend differ diff --git a/openpose.blend1 b/openpose.blend1 new file mode 100644 index 0000000..d473dda Binary files /dev/null and b/openpose.blend1 differ diff --git a/openpose.blend2 b/openpose.blend2 new file mode 100644 index 0000000..f815768 Binary files /dev/null and b/openpose.blend2 differ diff --git a/openpose.blend3 b/openpose.blend3 new file mode 100644 index 0000000..605b86c Binary files /dev/null and b/openpose.blend3 differ diff --git a/openpose.blend4 b/openpose.blend4 new file mode 100644 index 0000000..7c63dbe Binary files /dev/null and b/openpose.blend4 differ