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 datetimedef InitializeSdk():"""初始化SDK"""manager = FbxManager.Create()ios = FbxIOSettings.Create(manager, IOSROOT)manager.SetIOSettings(ios)return managerdef LoadFBX(manager, filename):"""加载FBX文件"""scene = FbxScene.Create(manager, "")importer = FbxImporter.Create(manager, "")if not importer.Initialize(filename, -1, manager.GetIOSettings()):return Noneif not importer.Import(scene):importer.Destroy()return Noneimporter.Destroy()return scenedef SafeCastToBlendShape(deformer):"""安全地将变形器转换为 BlendShape 对象"""try:if hasattr(FbxBlendShape, 'Cast'):return FbxBlendShape.Cast(deformer)else:return deformerexcept Exception as e:print(f"类型转换错误: {e}")return deformerdef 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_countfor 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_datadef process_blendshape_diff(file_a, file_b, output_file):"""生成HTML比较报告"""# 初始化SDKmanager = 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 Falseelse:with open(output_file, 'w', encoding='utf-8') as f:print("文件A和文件B的BlendShape数量一致\n")# in rangefor 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 Truedef 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 osdef 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 -1if 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 -1shape_keys = obj.data.shape_keys.key_blocksrenamed_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_nameprint(f"Renamed '{old_name}' to '{new_name}'")renamed_count += 1print(f"\nBatch renaming complete! Renamed {renamed_count} shape keys.")return renamed_countdef 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 = partsmap_dict[old_name] = new_nameelse:print(f"Warning: Line '{line}' is not in the expected format 'old_name new_name'. Skipping.")# 获取场景中的所有物体all_objects = bpy.data.objectsprint("Starting batch rename for all objects in scene...")print("=" * 50)for obj in all_objects:# 获取物体名称,取'.'前面的部分object_name = obj.namebase_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()