搜索内容

ESC 键关闭搜索框

blender批量导出不同动作模型

版块:Blender 发表于:2025-07-03

在同一个blend文件中有多个模型对象,而且有一套20多个动作库,我需要将这些不同的动作分别根据不同的模型导出成obj,手动指定动作然后导出,很麻烦,很耗时间,所以尝试写了一个脚本,仅供参考:

import bpy
import os
def export_complete_Bone_with_actions():
    """导出完整的Bone模型及其所有动作(包含所有身体部件)"""
    
    print("=== 完整Bone模型动作导出 ===")
    print(f"Blender版本: {bpy.app.version}")
    
    # 输出目录
    output_dir = "/Users/zhangbo/Desktop/BlenderExports"
    try:
        if not os.path.exists(output_dir):
            os.makedirs(output_dir)
        print(f"✓ 输出目录: {output_dir}")
    except Exception as e:
        print(f"✗ 创建输出目录失败: {e}")
        return
    
    # 21个动作名称
    action_names = [
        "00 Reset Pose",
        "01 Angry", 
        "02 Ballons in hands",
        "03 Fighting",
        "04 Happy_dancing 01",
        "05 Happy_dancing 02.001",
        "06 Jumping",
        "07 Meditating",
        "08 Mobile_in_hand",
        "09 Paint bucket",
        "10 Paper bag",
        "11 Paper in Hand",
        "12 Propose",
        "13 Running",
        "14 Sad",
        "15 Scared",
        "16 Shoked",
        "17 Sick",
        "18 Thumbs_up",
        "19 Unhappy",
        "20 Waving_hand"
    ]
    
    # === 调试:场景分析 ===
    print("\n=== 场景分析 ===")
    
    # 查找骨骼对象
    armature_obj = None
    for obj in bpy.context.scene.objects:
        if obj.type == 'ARMATURE':
            armature_obj = obj
            break
    
    if not armature_obj:
        print("✗ 未找到骨骼对象")
        return
    
    print(f"✓ 找到骨骼: {armature_obj.name}")
    
    # 查找Bone主体对象(更智能的查找方式)
    def find_Bone_object():
        """智能查找Bone主体对象"""
        # 1. 精确匹配
        Bone_obj = bpy.context.scene.objects.get("Bone")
        if Bone_obj and Bone_obj.type == 'MESH':
            return Bone_obj
        
        # 2. 名称包含Bone(不区分大小写)
        for obj in bpy.context.scene.objects:
            if obj.type == 'MESH' and 'Bone' in obj.name.lower():
                return obj
        
        # 3. 查找最大的网格对象
        max_verts = 0
        backup_obj = None
        for obj in bpy.context.scene.objects:
            if obj.type == 'MESH':
                verts = len(obj.data.vertices)
                if verts > max_verts:
                    max_verts = verts
                    backup_obj = obj
        
        return backup_obj
    
    Bone_obj = find_Bone_object()
    if not Bone_obj:
        print("✗ 未找到Bone主体对象")
        return
    
    print(f"✓ 找到Bone主体: {Bone_obj.name} (顶点数: {len(Bone_obj.data.vertices)})")
    
    # === 收集所有需要导出的对象 ===
    print("\n=== 收集导出对象 ===")
    
    export_objects = [Bone_obj]  # 首先添加Bone主体
    
    # 1. 从所有集合中收集对象(更全面的收集方式)
    body_part_keywords = [
        'face', 'body', 'leg', 'hand', 'arm', 
        'head', 'torso', 'eye', 'mouth', 'expression',
        'asset', 'accessory', 'prop'
    ]
    
    # 从所有集合中查找
    for collection in bpy.data.collections:
        print(f"检查集合: {collection.name}")
        for obj in collection.objects:
            if obj.type == 'MESH' and obj not in export_objects:
                # 检查是否是身体部件
                for keyword in body_part_keywords:
                    if keyword in obj.name.lower():
                        export_objects.append(obj)
                        print(f"✓ 添加 {obj.name} (来自集合 {collection.name})")
                        break
    
    # 2. 额外检查可能遗漏的对象(父子关系/顶点绑定)
    for obj in bpy.context.scene.objects:
        if obj.type == 'MESH' and obj not in export_objects:
            # 检查是否绑定到骨骼
            if obj.parent == armature_obj:
                export_objects.append(obj)
                print(f"✓ 添加 {obj.name} (绑定到骨骼)")
                continue
            
            # 检查修改器中是否有骨骼修改器
            for mod in obj.modifiers:
                if mod.type == 'ARMATURE' and mod.object == armature_obj:
                    export_objects.append(obj)
                    print(f"✓ 添加 {obj.name} (有骨骼修改器)")
                    break
    
    # 3. 最后添加骨骼
    if armature_obj not in export_objects:
        export_objects.append(armature_obj)
    
    print(f"\n将导出 {len(export_objects)} 个对象:")
    for obj in export_objects:
        print(f"  - {obj.name} ({obj.type})")
    
    # === 准备导出 ===
    print("\n=== 准备导出 ===")
    
    # 设置活动对象为骨骼
    bpy.context.view_layer.objects.active = armature_obj
    armature_obj.select_set(True)
    
    # 确保骨骼有动画数据
    if not armature_obj.animation_data:
        armature_obj.animation_data_create()
    
    # 确保所有导出对象可见且可被选择
    for obj in export_objects:
        obj.hide_set(False)
        obj.hide_viewport = False
        obj.hide_render = False
        obj.hide_select = False
    
    # === 开始导出每个动作 ===
    successful_exports = 0
    failed_exports = 0
    
    for i, action_name in enumerate(action_names):
        print(f"\n=== 进度 {i+1}/{len(action_names)}: {action_name} ===")
        
        # 查找动作
        action = bpy.data.actions.get(action_name)
        if not action:
            print(f"✗ 动作不存在,跳过")
            failed_exports += 1
            continue
        
        try:
            # 1. 应用动作到骨骼
            armature_obj.animation_data.action = action
            
            # 2. 设置到动作起始帧
            start_frame = int(action.frame_range[0]) if action.frame_range else 1
            bpy.context.scene.frame_set(start_frame)
            print(f"✓ 应用动作到第 {start_frame} 帧")
            
            # 3. 强制更新场景
            bpy.context.view_layer.update()
            bpy.context.evaluated_depsgraph_get().update()
            
            # 4. 清除选择并选择所有导出对象
            bpy.ops.object.select_all(action='DESELECT')
            selected_count = 0
            for obj in export_objects:
                try:
                    obj.select_set(True)
                    selected_count += 1
                except Exception as e:
                    print(f"⚠ 无法选择 {obj.name}: {e}")
            
            print(f"✓ 选择了 {selected_count}/{len(export_objects)} 个对象")
            
            # 5. 验证Bone对象是否被选中
            if Bone_obj not in bpy.context.selected_objects:
                print("⚠ Bone对象未被选中,强制选择")
                Bone_obj.select_set(True)
            
            # 6. 准备文件名
            safe_name = action_name.replace(" ", "_").replace(".", "_")
            filename = f"Bone_{safe_name}.obj"
            filepath = os.path.join(output_dir, filename)
            
            # 7. 导出OBJ(使用Blender 4.3的参数)
            print(f"导出到: {filename}")
            bpy.ops.wm.obj_export(
                filepath=filepath,
                export_selected_objects=True,
                export_animation=False,
                apply_modifiers=True,
                export_smooth_groups=False,
                export_normals=True,
                export_uv=True,
                export_materials=True,
                export_triangulated_mesh=False,
                export_curves_as_nurbs=False
            )
            
            # 8. 验证导出结果
            if os.path.exists(filepath):
                file_size = os.path.getsize(filepath)
                print(f"✓ 导出成功 ({file_size/1024:.1f} KB)")
                
                # 检查文件内容
                with open(filepath, 'r') as f:
                    content = f.read(1000)
                    if Bone_obj.name in content:
                        print(f"✓ 确认包含Bone对象")
                    else:
                        print(f"⚠ 警告: 可能未包含Bone对象")
                
                successful_exports += 1
            else:
                print(f"✗ 文件未创建")
                failed_exports += 1
                
        except Exception as e:
            print(f"✗ 导出失败: {str(e)}")
            failed_exports += 1
    
    # === 最终报告 ===
    print(f"\n{'='*60}")
    print(f"导出完成!")
    print(f"成功: {successful_exports} 个文件")
    print(f"失败: {failed_exports} 个文件")
    print(f"输出目录: {output_dir}")
    print(f"{'='*60}")
# 运行导出
if __name__ == "__main__":
    export_complete_Bone_with_actions()


如果你也想使用以上脚本,可能需要改几个地方:

  1. 修改其中的bone对象模型

  2. 修改动作名称

  3. 每个模型的结构不同,可能需要调整脚本代码,请自行尝试。有需要帮忙可以联系我

分享:

版权声明:

本贴内容采集自互联网。
严禁商用、严禁传播、严禁转载、严禁盗卖。

微信扫一扫