BlenderFBXExporter 导出fbx被修改问题

发布于:2025-07-04 ⋅ 阅读:(23) ⋅ 点赞:(0)

1) 解决增加A节点的问题

https://github.com/A-Ribeiro/CustomBlenderFBXExporter

2)找出blendshape 不一致,生成blendshape key name映射map 文件compare.txt

C:\Users\49938\Documents\DazToUnreal\zhang01\UpdatedFBX\zhang01_fix7.fbx
C:\Users\49938\Documents\DazToUnreal\zhang01\UpdatedFBX\zhang01.fbx
C:\Users\49938\Documents\DazToUnreal\zhang01\UpdatedFBX\compare.txt

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
BlendShape差异可视化报告生成器
生成HTML格式的BlendShape比较报告
"""

from fbx import *
import sys
import os
import json
from datetime import datetime


def InitializeSdk():
    """初始化SDK"""
    manager = FbxManager.Create()
    ios = FbxIOSettings.Create(manager, IOSROOT)
    manager.SetIOSettings(ios)
    return manager


def LoadFBX(manager, filename):
    """加载FBX文件"""
    scene = FbxScene.Create(manager, "")
    importer = FbxImporter.Create(manager, "")

    if not importer.Initialize(filename, -1, manager.GetIOSettings()):
        return None

    if not importer.Import(scene):
        importer.Destroy()
        return None

    importer.Destroy()
    return scene


def SafeCastToBlendShape(deformer):
    """安全地将变形器转换为 BlendShape 对象"""
    try:
        if hasattr(FbxBlendShape, 'Cast'):
            return FbxBlendShape.Cast(deformer)
        else:
            return deformer
    except Exception as e:
        print(f"类型转换错误: {e}")
        return deformer


def ExtractBlendShapeKeys(scene):
    """提取场景中所有的 BlendShape Key Names"""
    blendshape_data = {
        'keys': [],
        'meshes': {},  # 记录每个mesh的blendshape信息
        'total_channels': 0
    }

    def TraverseNode(node):
        try:
            mesh = node.GetMesh()
            if mesh:
                mesh_name = node.GetName()
                mesh_blendshapes = []

                deformer_count = mesh.GetDeformerCount()

                for i in range(deformer_count):
                    deformer = mesh.GetDeformer(i)
                    deformer_type = deformer.GetDeformerType()

                    if deformer_type == FbxDeformer.eBlendShape:
                        blend_shape = SafeCastToBlendShape(deformer)

                        try:
                            channel_count = blend_shape.GetBlendShapeChannelCount()
                            blendshape_data['total_channels'] += channel_count

                            for j in range(channel_count):
                                channel = blend_shape.GetBlendShapeChannel(j)
                                if channel:
                                    channel_name = channel.GetName()
                                    target_shape_count = channel.GetTargetShapeCount()

                                    if target_shape_count > 0:
                                        key_info = {
                                            'name': channel_name,
                                            'mesh': mesh_name,
                                            'target_count': target_shape_count
                                        }
                                        blendshape_data['keys'].append(key_info)
                                        mesh_blendshapes.append(channel_name)

                        except Exception as e:
                            print(f"处理 BlendShape 通道时出错: {e}")

                if mesh_blendshapes:
                    blendshape_data['meshes'][mesh_name] = mesh_blendshapes

            # 递归遍历子节点
            for i in range(node.GetChildCount()):
                TraverseNode(node.GetChild(i))

        except Exception as e:
            print(f"处理节点 '{node.GetName()}' 时出错: {e}")

    root_node = scene.GetRootNode()
    if root_node:
        TraverseNode(root_node)

    return blendshape_data


def process_blendshape_diff(file_a, file_b, output_file):
    """生成HTML比较报告"""
    # 初始化SDK
    manager = InitializeSdk()

    # 加载文件
    scene_a = LoadFBX(manager, file_a)
    scene_b = LoadFBX(manager, file_b)

    if not scene_a or not scene_b:
        print("错误: 无法加载文件")
        manager.Destroy()
        return False

    # 获取BlendShape信息
    print("正在分析文件A的BlendShape...")
    blendshapes_a = ExtractBlendShapeKeys(scene_a)
    print("正在分析文件B的BlendShape...")
    blendshapes_b = ExtractBlendShapeKeys(scene_b)

    blendshapes_a['keys']
    # 创建BlendShape名称列表
    names_a = [key['name'] for key in blendshapes_a['keys']]
    names_b = [key['name'] for key in blendshapes_b['keys']]

    if len(names_a) != len(names_b):
        print("❌ 错误: 文件A和文件B的BlendShape数量不一致")
        manager.Destroy()
        return False
    else:
        with open(output_file, 'w', encoding='utf-8') as f:
            print("文件A和文件B的BlendShape数量一致\n")
            
            # in range
            for i in range(len(names_a)):
                if names_a[i] != names_b[i]:
                    f.write(f"{names_a[i]}|{names_b[i]}\n")


    # 清理
    manager.Destroy()
    return True


def main():
    """主函数"""
    if len(sys.argv) < 3:
        print("🎭 BlendShape差异可视化报告生成器")
        print("\n📖 使用方法:")
        print("  python fbx_blendshape_diff_visualizer.py <fbxA> <fbxB> [blendshape_comparison.txt]")
        print("\n📋 参数:")
        print("  fbxA: 第一个FBX文件")
        print("  fbxB: 第二个FBX文件")
        print("  output.html: 可选 ")
        sys.exit(1)

    file_a = sys.argv[1]
    file_b = sys.argv[2]
    output_file = sys.argv[3] if len(sys.argv) > 3 else "blendshape_comparison.txt"

    # 检查文件
    if not os.path.exists(file_a):
        print(f"❌ 错误: 文件不存在 - {file_a}")
        sys.exit(1)

    if not os.path.exists(file_b):
        print(f"❌ 错误: 文件不存在 - {file_b}")
        sys.exit(1)

    print("🚀 开始分析BlendShape差异...")
    print(f"📁 文件A: {file_a}")
    print(f"📁 文件B: {file_b}")

    
    process_blendshape_diff(file_a, file_b, output_file)
 


if __name__ == "__main__":
    main()

 3)在blender里执行脚本修改blendshape key name


import bpy
import sys
import os

def rename_shape_keys_by_map(object_name, map_dict):
    """
    批量重命名指定物体的形状键,移除指定前缀
    
 
    """
    # 获取指定的物体
    obj = bpy.data.objects.get(object_name)
    
    if not obj:
        print(f"Error: Object '{object_name}' not found.")
        return -1
    
    if not obj.data or not hasattr(obj.data, 'shape_keys') or not obj.data.shape_keys:
        print(f"Error: Object '{object_name}' has no shape keys.")
        return -1
    
    shape_keys = obj.data.shape_keys.key_blocks
    renamed_count = 0
    
    # 从第二个key开始遍历 (跳过'Basis'基础键)
    for key in shape_keys[1:]:
        if key.name in map_dict:
            # 保存原名称用于打印
            old_name = key.name
            # 获取新名称
            new_name = map_dict[key.name]
            # 重命名
            key.name = new_name
            print(f"Renamed '{old_name}' to '{new_name}'")
            renamed_count += 1
 
    
    print(f"\nBatch renaming complete! Renamed {renamed_count} shape keys.")
    return renamed_count

def batch_rename_all_objects():
    """
    遍历场景中的所有物体,对每个物体执行形状键重命名操作
    
    对每个物体名称取'.'前面的部分作为基础名称,
    然后移除'_facs_ctrl'和'_facs_bs'前缀
    """
    
    map_path = r"C:\Users\49938\Documents\DazToUnreal\zhang01\UpdatedFBX\compare.txt"
    if not os.path.exists(map_path):
        print(f"Error: Map file '{map_path}' does not exist.")
        return -1
    # 读取文件的所有行
    with open(map_path, 'r', encoding='utf-8') as file:
        lines = file.readlines()
    # 创建一个字典来存储旧名称和新名称的映射
    map_dict = {}
    for line in lines:
        # 去除行首尾的空白字符
        line = line.strip()
        if not line:
            continue  # 跳过空行
        # 分割行,假设格式为 "旧名称|新名称"
        parts = line.split('|')
        if len(parts) == 2:
            old_name, new_name = parts
            map_dict[old_name] = new_name
        else:
            print(f"Warning: Line '{line}' is not in the expected format 'old_name new_name'. Skipping.")

    # 获取场景中的所有物体
    all_objects = bpy.data.objects
    
    print("Starting batch rename for all objects in scene...")
    print("=" * 50)
    
    for obj in all_objects:
        # 获取物体名称,取'.'前面的部分
        object_name = obj.name
        base_name = object_name.split('.')[0]
        
        print(f"\nProcessing object: '{object_name}' (base: '{base_name}')")
        
        # 对每个物体调用两次重命名函数
        rename_shape_keys_by_map(object_name, map_dict)
 
    
    print("\n" + "=" * 50)
    print("Batch processing complete!")
    print("=" * 50)
batch_rename_all_objects() 


网站公告

今日签到

点亮在社区的每一天
去签到