# 导出PSK插件地址 - https://github.com/Befzz/blender3d_import_psk_psa/releases - https://github.com/DarklightGames/io_scene_psk_psa/releases # 清空数据 bpy.ops.wm.read_factory_settings(use_empty=True) # PSK ```python import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * pskPath = 'C:\\Users\\BlueRose\\Desktop\\BLUEPROTOCOL\\Game\\Character\\Enemy' for root, subdirs, files in os.walk(pskPath): for dirName in subdirs: print(root + '\\'+ dirName) for modelroot, modeldirs, modelfiles in os.walk(root + '\\'+ dirName +'\\Model'): for f in modelfiles: if f.endswith('.psk'): psk_file = os.path.join(modelroot, f) fbx_file = os.path.splitext(psk_file)[0] + ".fbx" # 清空场景 bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() pskimport(psk_file, bReorientBones = False) # 使用SmoothShading for obj in bpy.data.objects: if obj.type=='MESH': obj.data.use_auto_smooth = True obj.data.auto_smooth_angle = math.radians(180) currentmesh = obj.data for setting in currentmesh.polygons: setting.use_smooth = True # 输出骨骼模型 bpy.ops.object.select_all(action='SELECT') bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False) ``` 直接生成fbx版本 ```python import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * pskPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\wp\\wp0100\\04_Mesh' for modelroot, modeldirs, modelfiles in os.walk(pskPath): for f in modelfiles: if f.endswith('.psk'): psk_file = os.path.join(modelroot, f) fbx_file = os.path.splitext(psk_file)[0] + ".fbx" # 清空场景 bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() pskimport(psk_file, bReorientBones = False) # 使用SmoothShading for obj in bpy.data.objects: if obj.type=='MESH': obj.data.use_auto_smooth = True obj.data.auto_smooth_angle = math.radians(180) currentmesh = obj.data for setting in currentmesh.polygons: setting.use_smooth = True # 输出骨骼模型 bpy.ops.object.select_all(action='SELECT') bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False) ``` 使用BetterExport的版本: ```python import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * from better_fbx import * pskPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\wp\\wp0100\\04_Mesh' for modelroot, modeldirs, modelfiles in os.walk(pskPath): for f in modelfiles: if f.endswith('.psk'): psk_file = os.path.join(modelroot, f) fbx_file = os.path.splitext(psk_file)[0] + ".fbx" # 清空场景 bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() pskimport(psk_file, bReorientBones = False) # 使用SmoothShading for obj in bpy.data.objects: if obj.type=='MESH': obj.data.use_auto_smooth = True obj.data.auto_smooth_angle = math.radians(180) currentmesh = obj.data for setting in currentmesh.polygons: setting.use_smooth = True bpy.ops.object.select_all(action='SELECT') bpy.ops.better_export.fbx(filepath=fbx_file,my_fbx_format="binary",use_selection=True,use_visible=True,use_only_root_empty_node=False) ``` # PSA 注意:该脚本因为Blender运行bug,会导致输出的FBX文件越来越大,越来越慢。一次转换的量不建议超过50个。建议分几个文件夹,同时开多个Blender一起转换。 ```python import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * pskPath = 'C:\\Users\\BlueRose\\Desktop\\BLUEPROTOCOL\\Game\\Character\\Enemy\\E000_00\\Model\\SK_CH_E000_00.psk' psaDirPath = 'C:\\Users\\BlueRose\\Desktop\\BLUEPROTOCOL\\Game\\Animations\\Enemy\\E000' # 清空场景并且导入模型 for root, subdirs, files in os.walk(psaDirPath): for f in files: if f.endswith('.psa'): bpy.ops.scene.new(type='EMPTY') bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True) psa_file = os.path.join(root, f) pskDirPath = os.path.dirname(pskPath) fbx_file = os.path.join(pskDirPath,f) fbx_file = os.path.splitext(fbx_file)[0] + ".fbx" print(fbx_file) psaimport(psa_file) bpy.ops.object.select_all(action='SELECT') bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False) bpy.ops.scene.new(type='EMPTY') bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() ``` **绯红结系批量转换代码**: ```python import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * from better_fbx import * for action in bpy.data.actions: bpy.data.actions.remove(action) # 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\04_Mesh\\SM_ch0100.pskx' # 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\no\\no0000\\04_Mesh\\SM_no0000_orbit.psk' pskPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\04_Mesh\\SM_ch0100.pskx' psaPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\25_SpAttack\\Battle\\c04\\AS_ElectricPole_ch0100_c04_ch0100.psa' fbxExportPath = 'C:\\Users\\BlueRose\\Desktop\\ScarletNexus\\Game\\Character\\ch\\ch0100\\25_SpAttack\\Battle\\c04\\' # 清空并且设置为60fps bpy.ops.scene.new(type='EMPTY') bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() bpy.context.scene.render.fps = 60 # 导入骨骼以及动画(所有NLA) pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True) psaimport(psaPath, bActionsToTrack = True) for obj in bpy.data.objects: print(obj.type) if obj.type=='ARMATURE': currentSkeleton = obj print(currentSkeleton.animation_data.action) for action in bpy.data.actions: # print(action) currentSkeleton.animation_data.action = action bpy.ops.better_export.fbx(filepath= fbxExportPath+ action.name + ".fbx",my_fbx_format="binary",use_selection=False,use_visible=True) # print(currentSkeleton.animation_data.action) # print(bpy.data.objects["SM_ch0100.ao"].animation_data.action) ``` **鸣潮批量转换脚本**: ```python import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * from better_fbx import * pskPath = 'C:\\Users\\BlueRose\\Desktop\\WutheringWaves\\Game\\Aki\\Character\\Role\\FemaleM\\Yangyang\\R2T1YangyangMd10011\\Model\\R2T1YangyangMd10011.psk' psaDirPath = 'C:\\Users\\BlueRose\\Desktop\\WutheringWaves\\Game\\Aki\\Character\\Role\\FemaleM\\Yangyang\\CommonAnim' # 清空场景并且导入模型 for root, subdirs, files in os.walk(psaDirPath): for f in files: if f.endswith('.psa'): # 清空并且设置为30fps(大部分是30fps) bpy.ops.scene.new(type='EMPTY') bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() bpy.context.scene.render.fps = 30 pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True) psa_file = os.path.join(root, f) psaDirPath = os.path.dirname(psa_file) fbx_file = os.path.join(psaDirPath,f) fbx_file = os.path.splitext(fbx_file)[0] + ".fbx" print(fbx_file) psaimport(psa_file) bpy.ops.object.select_all(action='SELECT') #bpy.ops.export_scene.fbx(filepath=fbx_file,add_leaf_bones=False) bpy.ops.better_export.fbx(filepath=fbx_file,my_fbx_format="binary",use_selection=False,use_visible=True) bpy.ops.scene.new(type='EMPTY') bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() ``` ## 添加平滑组 ```python import bpy import os import math for obj in bpy.data.objects: if obj.type=='MESH': obj.data.use_auto_smooth = True obj.data.auto_smooth_angle = math.radians(180) currentmesh = obj.data for setting in currentmesh.polygons: setting.use_smooth = True ``` ## NLA Action遍历 ```c++ import bpy import os import math for action in bpy.data.actions: print(action) action.name bpy.data.actions["AS_ch0100_016_AL_walk"].name = "AS_ch0100_016_AL_walk" bpy.context.object.animation_data.nla_tracks.active = bpy.context.object.animation_data.nla_tracks[0] # You can set an object's action like: # object.animation_data.action = action print(bpy.data.objects["SM_ch0100.ao"].animation_data.action) ``` ``` 取得当前激活状态的NLA Action ```python for i in bpy.data.objects: for j in i.animation_data.nla_tracks.values(): for k in j.strips.values(): if k.active: print(k) ``` # 其他 获取UModel解包游戏所需的AES Key:https://cs.rin.ru/forum/viewtopic.php?t=100672 ## 蓝色协议 AES Key:0xEA9051DDACE1CCF98A0510F0E370BD986A75C74756E0309E6A578A47AF564255 UE版本为:4.27.2 ```c++ D:\OtherTools\GameAssets\umodel_win32_1590\umodel_64.exe -path=D:\Games\BlueProtocol\BLUEPROTOCOL -out=C:\Users\BlueRose\Desktop\BlueProtocol\ -game=ue4.27 -aes=0xEA9051DDACE1CCF98A0510F0E370BD986A75C74756E0309E6A578A47AF564255 ``` - 角色参考: https://wiki.biligame.com/blueprotocol/%E4%BA%BA%E7%89%A9 - Game - Character - Enpc - F100_fst00(开头女配) - Anim - AS_C000_BLS_LOC_AimRunFC_B ## 绯红结系 AES Key:0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540 UE版本为:4.26 ```bash D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -path=C:\Game\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540 D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -path=D:\SteamLibrary\steamapps\common\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540 D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_legacy.exe -path=D:\SteamLibrary\steamapps\common\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540 ``` 需要特殊的UModel进行提取:https://www.gildor.org/smf/index.php/topic,7814.0.html#msg40636 下载地址:https://drive.google.com/file/d/1S_037OmnxY28cQymu8NMwFApsBahFqx6/view 导出动画方法: 1. 选择骨骼模型,右键点击Open(add to locate set),之后按o。 2. 选择动画资产(AS开头),右键点击Open(add to locate set)。 3. 点击Tools - ExportCurrentObject,就可以导出指定的动画。 4. 如果选择多个动画的情况下,在Blender psk/psa插件中可以勾选`All actions to NLA track` 5. 导出动画时,在烘焙动画选项中选择全部动作、NLA片段。所有动画数据都会以NLA Clip的方式存储。 6. 导入UE就可以显示所有的动画数据。 **导出的时候还需要注意:1、FPS是不是设置成60,默认是24。2、导出选项需要去掉添加叶子骨骼节点的选项** ### 资产笔记 角色模型分为Ch与Co前缀,Ch是Ch/Chxxx中的模型,Co是00_Common/Coxxx中的模型。 - Effect:游戏特效,里面的一些贴图与Mesh可以拿来做刀光。 - Character - 00_Common:主角插在身上的Link线以及其他长在怪物身上的电灯泡与花。 - ch:各种角色相关资产。 - Animation:各种动画。 - 400_DM:角色受到攻击。 - - SAS:男主的SAS技能。 - BrainCrash:击破处决动画。 - DriveMode:驱动模式。(随机触发的爆发状态) - BrainField:一些Qte 伤害动画。(主要是一些Boss的) - GameOver:游戏失败的角色动画。 - SPAttack:游戏中的SP攻击,也就是Qte念力攻击。 - Intercept:拦截游戏中另一个主角的SPAttack。 - wp:各个角色的武器 - wp0100:男主武器。 - wp0200:女主武器。 - wp0300:花火武器。棍子 - wp0400:拳套。 - wp0800:警棍。 - wp0900:手弩。 - wp1000:电锯。 - wp1100:手里剑。 - em:怪物模型。 - BrainCrash:里面主要是怪物处决的Qte动画。AS_BC_emxxxx_前缀后面代表对应的角色模型。 - np:NPC模型。 长得像石头的 - /Game/Character/00_Common/04_Mesh/SM_CableEnergyMesh.uasset - /Game/Character/no/no0000/04_Mesh/SM_no0000_orbit.uasset - /Game/Character/00_Common/co0300/04_Mesh/SM_co0300.uasset - /Game/DLC/Ver0102/Environment/Location/LC70/SM_pr_LC70_Block.uasset # UModel 快捷键: - h:显示所有命令。 - 空格:播放动画。 - s:显示骨骼。 - 【】:选择下/上一个动画。 - 《》:选择下/上一帧。 ## CMD启动 ```bash D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -path=C:\Game\ScarletNexus -out=C:\Users\BlueRose\Desktop\ScarletNexus -game=ue4.26 -aes=0x48AE8311350417BDC50A440FCD0E98B2FA6BCEAE3EDA8D0E24881F205E6C4540 ``` 显示所有命令选项: ```bash D:\OtherTools\GameAssets\umodel_win32_1590\umodel_scarlet_nexus_v5.exe -help ``` ```bash Usage: umodel [command] [options] [ []] umodel [command] [options] umodel @ name of package to load - this could be a file name with or without extension, or wildcard name of object to load class of object to load (useful, when trying to load object with ambiguous name) path to the game (see -path option) Commands: -view (default) visualize object; when no specified will load whole package -list list contents of package -export export specified object or whole package -save save specified packages Help information: -help display this help page -version display umodel version information -taglist list of tags to override game autodetection (for -game=nnn option) -gamelist list of supported games Developer commands: -log=file write log to the specified file -dump dump object information to console -pkginfo load package and display its information -testexport perform fake export Options: -path=PATH path to game installation directory; if not specified, program will search for packages in current directory -game=tag override game autodetection (see -taglist for variants) -pkgver=nnn override package version (advanced option!) -pkg=package load extra package (in addition to ) -obj=object specify object(s) to load -gui force startup UI to appear -aes=key provide AES decryption key for encrypted pak files, key is ASCII or hex string (hex format is 0xAABBCCDD), multiple options could be provided for multi-key games -aes=@file.txt read AES decryption key(s) from a text file Compatibility options: -nomesh disable loading of SkeletalMesh classes in a case of unsupported data format -noanim disable loading of MeshAnimation classes -nostat disable loading of StaticMesh class -novert disable loading of VertMesh class -notex disable loading of Material classes -nomorph disable loading of MorphTarget class -nolightmap disable loading of Lightmap textures -sounds allow export of sounds -3rdparty allow 3rd party asset export (ScaleForm, FaceFX) -lzo|lzx|zlib force compression method for UE3 fully-compressed packages Platform selection: -ps3 Playstation 3 -ps4 Playstation 4 -nsw Nintendo Switch -ios iOS (iPhone/iPad) -android Android Viewer options: -meshes view meshes only -materials view materials only (excluding textures) -anim= specify AnimSet to automatically attach to mesh Export options: -out=PATH export everything into PATH instead of the current directory -all used with -dump, will dump all objects instead of specified one -uncook use original package name as a base export directory (UE3) -groups use group names instead of class names for directories (UE1-3) -uc create unreal script when possible -psk use ActorX format for meshes (default) -md5 use md5mesh/md5anim format for skeletal mesh -gltf use glTF 2.0 format for mesh -lods export all available mesh LOD levels -dds export textures in DDS format whenever possible -png export textures in PNG format instead of TGA -notgacomp disable TGA compression -nooverwrite prevent existing files from being overwritten (better performance) ``` ## 取得资产路径 1. 批量选择动画资产 2. 右键点击Copy package path /Game/Character/ch/ch0200/05_Animation/AS_ch0200_906_FC_smile_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_901_FC_default_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_906_FC_smile_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_911_FC_angry_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_916_FC_sad_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_921_FC_surprise_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_926_FC_fear_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_931_FC_aversion_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_936_FC_damage_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_937_FC_dead.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_941_FC_serious_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_946_FC_blushing_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_951_FC_ex01_m.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose001_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose002_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose003_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose004_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose005_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose006_ST.uasset /Game/Character/ch/ch0200/05_Animation/AS_ch0200_AL_pose007_ST.uasset # 解决Blender导出问题的根骨骼问题 使用Blender的**Better Fbx Importer & Exporter** addon就可以解决问题。 # Umodel源码 - https://github.com/gildor2/UEViewer - 其他版本 - 支持[ACL 压缩](https://github.com/nfrechette/acl)的版本:https://www.gildor.org/smf/index.php/topic,7906.0.html - 2021.11_Material:https://drive.google.com/file/d/1oJVuux9XKXQttljL5ncZIF8_5O4CLfh0/view?usp=sharing - 获取链接:https://www.gildor.org/smf/index.php/topic,7906.0.html - 2024.7使用较新版本ACL:https://drive.google.com/file/d/16qvYfhGg-yD7Q_FKQzuImO-dMH2s_m3u/view?usp=sharing - 2024.7使用较旧版本ACL:https://drive.google.com/file/d/1Z6SJ55Xf_lemmVpps_YsS6QsiQJHDr-j/view?usp=sharing - https://github.com/mdro/UEViewer - https://www.gildor.org/smf/index.php/topic,8565.0.html # UModel提取声音教程 https://www.gildor.org/smf/index.php/topic,6389.msg34339.html#msg34339 https://www.gildor.org/smf/index.php?topic=7271.0 https://www.youtube.com/watch?v=Nxl0OcoArZ8 1.  使用 Umodel 或 QuickBMS 与 Unreal Tournament 4 脚本来提取 UEXP 1. 下载QuickBMS https://aluigi.altervista.org/quickbms.htm 2. 使用 VGMToolbox 在其“Common Archives”部分 。中解压 UEXP 。`VGMToolbox\Misc.Tools\Extraction Tools\Common Archives\CRI ACB/AWB Archive Extractor`选项,并将所有提取的 UEXP 包拖到右侧窗口中。 1. [https://sourceforge.net/projects/vgmtoolbox/](https://sourceforge.net/projects/vgmtoolbox/) 3. 这将获取 .bin可以使用“Streams”部分中的 VGMToolbox 的 HCA 提取器将这些文件提取为 HCA。 4. 然后可以使用以下任何工具将 HCA 转换为 wav: 1. Foobar with VGMStream [http://www.foobar2000.org/download](http://www.foobar2000.org/download) [http: //www.foobar2000.org/components/view/foo_input_vgmstream](http://www.foobar2000.org/components/view/foo_input_vgmstream) 2. Thealexbarney 的 VGAudio [https://github.com/Thealexbarney/VGAudio](https://github.com/Thealexbarney/VGAudio) 3. Nyagamon 的 HCADecoder [https://github.com/Nyagamon/HCADecoder](https://github.com/Nyagamon/HCADecoder) ffmpeg -i input.awb -acodec libmp3lame -ab 192k output.mp3 # 鸣潮 鸣潮个版本ASE:https://github.com/ClostroOffi/wuwa-aes-archive | Version | Operating System | Build | Server | Main AES Keys | pakchunk-5 AES Keys | Status | | :-----: | :--------------: | :---------------------------------: | :-----: | :----------------------------------------------------------------: | :-----------------------------------------------------------------: | :--------: | | 0.1.0 | Windows | Technical Test Build | CN | 0x773630F7D0C0516311D0598C538EB94AABAD423208F3633A98E34513557DE2C5 | None | Functional | | 0.7.0 | Windows | CBT1 (Closed Beta Test) Build | OS & CN | 0x3CD6493EFB7AE59DC3452D0F9CFF44D2CAD4BDC1FBA00DF7CC4FA4707A81AD75 | None | Functional | | 0.8.0 | Windows | pre-CBT2 (NDA Test Build) | CN | 0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F | 0xAEA4CBC6E1B6CCCD49F2426087AFDDC1F84662B45019BB6CBFFD62F470AFCDD5 | Functional | | 0.9.0 | Windows | CBT2 (Closed Beta Test) Build | OS & CN | 0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F | 0xF0ABFF9E59F0A1C5723AA146CBC190B641E5528E18F2AF1AA364A741E80E3EC8 | Functional | | 0.10.0 | Windows | Localization Pre-release Test Build | CN | 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 | None | Functional | | 1.0 | Windows | Release Build | OS | 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 | 0xE8F50588DEDAE3C5158E78FA7349A2C1E09CEC20B4A0FA4B2CF82C0ADDEFE2EA | Functional | | 1.0 | Android | Release Build | OS | 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 | 0x3BE656CDF13186E14E15E2A088FDB6D888DF8890501305F882B6751D655BC91D | Untested | | 1.0 | iOS & iPad OS | Release Build | OS | 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 | 0x78D8F3814E6EB5AE71466DEDE1BA6F1C22DB5769455924B9D10A4C3E89E364D9 | Untested | | 1.0 | Windows | Release Build | CN | 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 | 0x030115E0B73D89503D64D89FA4CA4966C16FC3B366AEE6A1BE3954F5EDD09661 | Functional | | 1.0 | Android | Release Build | CN | 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 | 0x9D0B462C529DBA54DF23E18FAA758591798ED477AC664316F1C1F97992F0C3CE | Untested | | 1.0 | iOS & iPad OS | Release Build | CN | 0xE0D4C0AA387A268B29C397E3C0CAD934522EFC96BE5526D6288EA26351CDACC9 | 0x71278FF9F0C0B607908EFCAC4EA412E763144F011746EE0BF55A86EE950DE787 | Untested | | 1.1 | Windows | Beta Build | CN | 0x47445D63F10E5EB004B8D3352A58C3E8444E1F7D1907A442D204161C71C567DC | None | Functional | | 1.1 | Windows | Release Build | OS | 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 | 0x52B3F2003A28C3145C98866BEECC3F884051140E03CC42946A89DB126AD55E9C | Functional | | 1.1 | Android | Release Build | OS | 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 | 0xB7370A8CB4BF5BECACA1325DDC0FF17F7E20C1145962D2F5CEA4CD0BB984EE | Untested | | 1.1 | iOS & iPad OS | Release Build | OS | 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 | 0x274CEFEA28768C320ECA33736968D98EE31DEC6C32536A862C8E7954EA0EB3F7 | Untested | | 1.1 | Windows | Release Build | CN | 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 | 0xC6F1A514CC58A88C1FE934E93DBD6E532EA5A00C2A2C9E4AE7774832BFF0686D | Functional | | 1.1 | Android | Release Build | CN | 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 | 0x6926CEAC6639B4E18DA566BB00BC4F0BE67E98768E4121C733F3AC033E1D4586 | Untested | | 1.1 | iOS & iPad OS | Release Build | CN | 0x43C51CC2369B9DD195EDCF426C78E30E99D7514DC14E8C03A831E128A3941010 | 0xA10A653F18F3497FABC2F8EC39EACA8AC3379CD53B92CEA17C05B41A0C4507C8 | Untested | | 1.2 | Windows | Beta Build | CN | 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 | None | Functional | | 1.2 | Windows | Release Build | OS | 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 | 0xB8B2D6B3DE6DA30113D7139BA95BD62E5E91EEAAAA3EBA7F7CD8261EEAA7F992 | Functional | | 1.2 | Android | Release Build | OS | 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 | 0x1EB5B065960D66252A35CD1AC712078E8F471C0BA93B0CFFFC958F3D2112B9F1F | Untested | | 1.2 | iOS & iPad OS | Release Build | OS | 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 | 0xCCCCC77B8CE81898DA9AE9FDBF7AB5B2A80A70C38177A5999010C75193586231 | Untested | | 1.2 | Windows | Release Build | CN | 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 | 0x1454A21FD285F51959AF0164EEBD8AE81D725BF499F13525539E7A1568F0E9D8 | Functional | | 1.2 | Android | Release Build | CN | 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 | 0xE8A45AF0BB37E139C328309340E0958292A8A07007FE7E3A771AA8D74099F5D6 | Untested | | 1.2 | iOS & iPad OS | Release Build | CN | 0x4D65747EDEB74A1DE116B1DD147CF79CD6C082F0DB7908E1BBD37F0428426469 | 0x6651BB4632FE9D8FC5B78BF8F41DB0DF4EFFB338C127434CD093D6AA368050E3 | Untested | | 1.3 | Windows | Beta Build | CN | 0x63B32F1F0DFB84CD4763EA5BC430D305AFDF126774CC3CCDB6CE0CEF3115256F | None | Functional | | 1.3 | Windows | Release Build | OS | 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 | 0xBDA94387C9E7F157CE31B25144A2DF54E3A1BF011A69E86485470D4132F23BC0 | Functional | | 1.3 | Android | Release Build | OS | 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 | TBD | Untested | | 1.3 | iOS & iPad OS | Release Build | OS | 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 | TBD | Untested | | 1.3 | Windows | Release Build | CN | 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 | TBD | Functional | | 1.3 | Android | Release Build | CN | 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 | TBD | Untested | | 1.3 | iOS & iPad OS | Release Build | CN | 0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 | TBD | Untested | | 1.4 | Windows | Beta Build | CN | 0xF031394DB4776A6DF94A04DA25774655406C4A6C6941C3788FB51C2D423206C2 | None | Functional | https://www.gildor.org/smf/index.php?topic=8258.0 UE4 version: 4.26 Wuthering Waves (beta)  0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F ```c++ //D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\WutheringWaves -out=C:\Users\BlueRose\Desktop\WutheringWaves -game=wuther -aes=0xF259C330E6B308BF34086CF30013241A1277F6E25D8F580746C2E8829EA1E15F //D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\WutheringWaves -out=C:\Users\BlueRose\Desktop\WutheringWaves -game=wuther -aes=0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 ``` 国际服 ```c++ D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\WutheringWaves_global -out=C:\Users\BlueRose\Desktop\WutheringWaves -game=wuther -aes=0x0B3915926D75EC12C602DB59000FC1F8AD5BA45D3A6A5B1675E4B7B54E703684 ``` 以秧秧为例: - 模型位于:/Game/Aki/Character/Role/FemaleM/Yangyang/R2T1YangyangMd10011/Model - 动画: - 公共动画:/Game/Aki/Character/Role/FemaleM/BaseAnim - 角色动画:/Game/Aki/Character/Role/FemaleM/Yangyang/CommonAnim/ - Attack01~05 - AirAttack系列 - Burst01 - Skill01~02 - SkillQte - Stand1_Action01~03 - Stand2 - StandChange - AimOffset动画:/Game/Aki/Sequence/SequenceAnim/DialogAnim/FemaleM/Yangyang/AnimOffset - 身体动画(大量动画):/Game/Aki/Sequence/SequenceAnim/DialogAnim/FemaleM/Yangyang/Body - cs(文件夹): Apology01、Promise、Walk - fuyong(文件夹):Walk、Run - 面部相关动画:/Game/Aki/Sequence/SequenceAnim/DialogAnim/FemaleM/Yangyang/Face - ShengQi02 - UI相关动画:/Game/Aki/Character/Role/FemaleM/Yangyang/CommonAnim/UIAnim - 角色飘带动画:/Game/Aki/Character/Role/FemaleM/Yangyang/R2T1YangyangMd10011/RibbonAnim 植被模型存在较大概率提取出错的问题 - Scene - Assets - [x] Common - [x] Levels - [x] HeiHaiAn - [x] HuangLong - [ ] FuBen - [x] FB_ZheZhi - [ ] - [x] LiangDao - [x] WuYinQu - [ ] ~~PCG~~ - [ ] SkyBox - [ ] Temp - [ ] # 罪恶装备 1. Guilty Gear: Strive 0x3D96F3E41ED4B90B6C96CA3B2393F8911A5F6A48FE71F54B495E8F1AFD94CD73 2. UE 4.25 ```c++ //经过测试可以导出 D:\OtherTools\GameAssets\umodel_win32_1590\umodel_64.exe -path=D:\Games\Guilty.Gear.STRIVE.Deluxe.Edition -out=C:\Users\BlueRose\Desktop\GuiltyGear -game=ue4.25+ -aes=0x3D96F3E41ED4B90B6C96CA3B2393F8911A5F6A48FE71F54B495E8F1AFD94CD73 ``` 提取模型参考: - https://zhuanlan.zhihu.com/p/644379193 - 贴图:https://zhuanlan.zhihu.com/p/493802718 ```python import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * from better_fbx import * for action in bpy.data.actions: bpy.data.actions.remove(action) pskPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Mesh\\zet_weapon_kaihou.psk' psaPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Animation\\Default\\weapon\\zet001.psa' fbxExportPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Animation\\Default\\weapon\\' # 清空并且设置为60fps bpy.ops.scene.new(type='EMPTY') bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() bpy.context.scene.render.fps = 60 # 导入骨骼以及动画(所有NLA) pskimport(pskPath, bReorientBones = False, bImportmesh = False, bImportbone = True) psaimport(psaPath, bActionsToTrack = True) for obj in bpy.data.objects: print(obj.type) if obj.type=='ARMATURE': currentSkeleton = obj print(currentSkeleton.animation_data.action) for action in bpy.data.actions: # print(action) currentSkeleton.animation_data.action = action bpy.ops.better_export.fbx(filepath= fbxExportPath+ action.name + ".fbx",my_fbx_format="binary",use_selection=False,use_visible=True) # print(currentSkeleton.animation_data.action) # print(bpy.data.objects["SM_ch0100.ao"].animation_data.action) # 导入模型 import bpy import os import math from io_import_scene_unreal_psa_psk_280 import * from better_fbx import * for action in bpy.data.actions: bpy.data.actions.remove(action) pskPath = 'C:\\Users\\BlueRose\\Desktop\\GuiltyGear\\Chara\\\BGT\\Costume01\\Mesh\\bgt_body.psk' # psaPath = 'C:\\Users\\BlueRose\\Desktop\\GranblueFantasyVersusRising\\Chara\\ZET\\Costume01\\Animation\\Default\\weapon\\zet001.psa' fbxExportPath = 'C:\\Users\\BlueRose\\Desktop\\GuiltyGear\\Chara\\BGT\\Costume01\\Mesh\\' # 清空并且设置为60fps bpy.ops.scene.new(type='EMPTY') bpy.ops.object.select_all(action='SELECT') bpy.ops.object.delete() bpy.context.scene.render.fps = 60 # 导入骨骼以及动画(所有NLA) pskimport(pskPath, bReorientBones = False, bImportmesh = True, bImportbone = True) # psaimport(psaPath, bActionsToTrack = True) fileName = os.path.basename(pskPath) bpy.ops.better_export.fbx(filepath= fbxExportPath + fileName.split('.')[0] + ".fbx",my_fbx_format="binary",use_selection=False,use_visible=True) ``` ## 提取角色 - BGT:修女 布丽姬 https://baike.baidu.com/item/%E5%B8%83%E4%B8%BD%E5%A7%AC/61832042 - [【罪恶装备.斗争】布丽姬.开场.结算.挑衅.合集.BGM版](https://www.bilibili.com/video/BV1Kt4y1G71Z/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e) - 【【罪恶装备.斗争】布丽姬.开场.结算.挑衅.合集.BGM版】 【精准空降到 02:39】 https://www.bilibili.com/video/BV1Kt4y1G71Z/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e&t=159 - Anim - BGT_p007_body01 - bgt000_body - INO:吉他女巫 茵诺 https://baike.baidu.com/item/%E8%8C%B5%E8%AF%BA/61512654 - May:水手服少女 梅伊 https://baike.baidu.com/item/%E6%A2%85%E4%BC%8A/61546254 - Anim - may000_body01 - may001_body01 - SOL:男主? - Anim - sol000_body01 - sol001_body01 - RAM:双巨剑 拉姆 https://baike.baidu.com/item/%E6%8B%89%E5%A7%86%E8%95%BE%E8%90%A8%E5%B0%94%C2%B7%E5%8D%8E%E4%BC%A6%E6%B3%B0/61440950 - Anim - ram000_body01 - JKO:Jack-O - Anim - jko010_body01 - jko011_body01 - jko_m902_body01 - JKO_p001_body01 - BGT【罪恶装备Strive[个人翻译]布里吉特主题曲 The Town Inside Me】 https://www.bilibili.com/video/BV1VN4y1V7Z9/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e - JKO【罪恶装备Strive[个人翻译]杰克欧角色主题曲 Perfection Can't Please Me】 https://www.bilibili.com/video/BV17541117o3/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e - 地图 - 沙漠戈壁 - Map/Battle/Wildemess # 碧蓝幻想Rising 1. Granblue Fantasy Versus 0x2A472D4B6150645367566B597033733676397924423F4528482B4D6251655468 2. UE 4.21 ```c++ //经过测试可以导出 D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials.exe -path=D:\Games\GranblueFantasyVersusRising -out=C:\Users\BlueRose\Desktop\GranblueFantasyVersusRising -game=ue4.27 -aes=0x6470C12A9B471BBA1D89A72D4F9B84EF709A65B88A85F240B8E99CD631751437 ``` **需要特殊构建版本** https://www.gildor.org/smf/index.php/topic,7906.0.html ## 提取角色 - ZET 赛达 - zet000 - zet001 - NRM 梅露娜 - nrm400 - nrm000 - DJT 姬塔 - djt000 - djt610 - CAL 卡莉奥丝特罗 - cal000 - 2B - twb000 - YUE 尤艾尔 - yue000 # 破晓传说 - Tales of Arise 0x61FC71A1376A29598393E2BFB976B275705F460E29399CFD1F9C060C8CD6CE40 - UE 4.20 ? 4.25? - UModel 1571 # 碧蓝幻想Relink https://www.bilibili.com/video/BV15M4m197TG/?spm_id_from=333.337.search-card.all.click&vd_source=f333e2edf5f07eeccd47911824e738a3 # 尘白禁区 umodel_acl_2.1 UE4.26 0xC14735FB5A872D2AFA76A5C38521AB8B8E21072C08525B913307608BD1182FA7 https://live2dhub.com/t/topic/3103/11 # 少女前线2 https://github.com/nijinekoyo/GF2AssetBundleDecryption https://blog.csdn.net/weixin_45346745/article/details/137476906 # 原神 & 星穹铁道 - `Star Rail\Game\StarRail_Data\StreamingAssets\Asb\Windows` - `Genshin Impact\Genshin Impact Game\YuanShen_Data\StreamingAssets` - [ ] 1.36 ## 原神 - 贴图参考:https://zhuanlan.zhihu.com/p/511540455 # CaravanSandwitch D:\OtherTools\GameAssets\umodel_win32_1590\umodel_materials_ue5.exe -path=D:\Games\CaravanSandwitch -out=C:\Users\BlueRose\Desktop\CaravanSandwitch -game=ue5.4 # Pak反汇编&UModel - 虚幻4(ue4)引擎加密pak解包教程(初学者向x64源码逆向) - 看雪的文章 - 知乎 https://zhuanlan.zhihu.com/p/96480632 - 支持Addtition动画的版本: https://github.com/djhaled/UEViewer - 支持ACL的UModel https://github.com/MinshuG/UEViewer # Pak AdditiveAnimation 恢复 - https://www.bilibili.com/video/BV13r4y1a7mW/?vd_source=f333e2edf5f07eeccd47911824e738a3 ## 脚本分析 ### BaseAnimation 主要逻辑: 1. 根据路径导入fbx文件。 2. 获取所有骨骼数据。 3. 骨骼进行缩放。 ```c++ on BtnBase pressed do ( local filename = getOpenFileName types:"Base Animation (*.fbx)|*.fbx|All (*.*)|*.*|" filename:g_lastDir1 if filename != undefined then ( BaseAnimFileName = filename g_lastDir1 = getFilenamePath BaseAnimFileName if DoesFileExist BaseAnimFileName then ( importFile BaseAnimFileName #noPrompt using:Wavefront_Object local BaseAnimBones = FindAllBones() scale BaseAnimBones[1] [(50.0/127.0),(50.0/127.0),(50.0/127.0)] ) ) ) ``` ### AdditiveAnimation 将路径正确的AdditiveAnimation动画路径加入LstAnims中。 ```c++ Anims = #() if DotNetObject == undefined then ( local filename = getOpenFileName types:"Additive Anim (*.fbx)|*.fbx|All (*.*)|*.*" filename:g_lastDir2 if filename != undefined then ( AddAnimFileName = filename g_lastDir2 = getFilenamePath AddAnimFileName ) ) else ( local filenames = getMultiOpenFilenames types:"Additive Anim (*.fbx)|*.fbx|All (*.*)|*.*" filename:g_lastDir2 if filenames != undefined then ( for filename in filenames do ( AddAnimFileName = filename g_lastDir2 = getFilenamePath AddAnimFileName if DoesFileExist AddAnimFileName then ( ImportAddAnimFile filename LstAnims.items = for a in Anims collect (a.Name) ) ) ) ) ``` ### ExportAnimation ***核心逻辑***: BtnExport遍历AdditiveAnim,并调用CalculateAddRotation。 追要参数为: - XMap - YMap - ZMap - AixMap - boneNum(是否包含pelvis骨骼) ```c++ on BtnExport pressed do ( BoneRotationArray = #() if (LstAnims.items.count>0 and BaseAnimFileName != undefined) then ( XMap = DDLX.selection YMap = DDLY.selection ZMap = DDLZ.selection AixMap = DDLAix.selection if g_pelvis then ( boneNum = 1 ) else boneNum = 2 for i=1 to LstAnims.items.count do ( CalculateAddRotation i ) ) ) ``` CalculateAddRotation: ```c++ fn CalculateAddRotation index = ( //导入AdditiveAnim importFile Anims[index].Address #noPrompt using:Wavefront_Object local bones = FindAllBones() //清空数组 BoneRotationArray = #() //遍历骨骼,从第二根骨骼,也就是Pelvis开始。(仅仅针对UE骨骼,Bip得重新考虑) for j=1 to bones.count do ( if j>boneNum then ( //计算当前骨骼相对父骨骼的相对旋转。 local Value_X = ((at time 0 bones[j].rotInParent).x - (at time 0 bones[j].parent.rotInParent).x) local Value_Y = ((at time 0 bones[j].rotInParent).y - (at time 0 bones[j].parent.rotInParent).y) local Value_Z = ((at time 0 bones[j].rotInParent).z - (at time 0 bones[j].parent.rotInParent).z) //针对XYZ情况的不同,将数据存入BoneRotationArray数组。 if (abs Value_X) > 0.05 then ( if (abs Value_Y) > 0.05 then ( if (abs Value_Z) > 0.05 then ( append BoneRotationArray (SaveValue j bones[j].name Value_X Value_Y Value_Z) ) else ( append BoneRotationArray (SaveValue j bones[j].name Value_X Value_Y 0) ) ) else ( if (abs Value_Z) > 0.05 then ( append BoneRotationArray (SaveValue j bones[j].name Value_X 0 Value_Z) ) else ( append BoneRotationArray (SaveValue j bones[j].name Value_X 0 0) ) ) ) else ( if (abs Value_Y) > 0.05 then ( if (abs Value_Z) > 0.05 then ( append BoneRotationArray (SaveValue j bones[j].name 0 Value_Y Value_Z) ) else ( append BoneRotationArray (SaveValue j bones[j].name 0 Value_Y 0) ) ) else ( if (abs Value_Z) > 0.05 then ( append BoneRotationArray (SaveValue j bones[j].name 0 0 Value_Z) ) ) ) ) ) --Set Base Animation Rotation //导入BaseAnim if DoesFileExist BaseAnimFileName then ( importFile BaseAnimFileName #noPrompt using:Wavefront_Object local BaseAnimBones = FindAllBones() scale BaseAnimBones[1] [(50.0/127.0),(50.0/127.0),(50.0/127.0)] //调用RotationBone()来设置新的旋转数值 case AixMap of ( 1:( RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones) 2:( RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones) 3:( RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones) 4:( RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones) 5:( RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones) 6:( RotationBone 0 0 1 BoneRotationArray ZMap BaseAnimBones RotationBone 0 1 0 BoneRotationArray YMap BaseAnimBones RotationBone 1 0 0 BoneRotationArray XMap BaseAnimBones) ) ExportAnim Anims[index].Address ) ) ``` RotationBone(): ```c++ fn RotationBone X Y Z BoneArray Map BaseAnimBones = ( local RotationValue = 0 for a=1 to BoneArray.count do ( case Map of ( 1:(RotationValue = BoneArray[a].ValueX) 2:(RotationValue = BoneArray[a].ValueY) 3:(RotationValue = BoneArray[a].ValueZ) 4:(RotationValue = (BoneArray[a].ValueX)*(-1)) 5:(RotationValue = (BoneArray[a].ValueY)*(-1)) 6:(RotationValue = (BoneArray[a].ValueZ)*(-1)) --default:(RotationValue = BoneRotationArray[a].ValueX) ) in coordsys world rotate BaseAnimBones[(BoneArray[a].Index)] (EulerAngles (RotationValue*X) (RotationValue*Y) (RotationValue*Z)) ) ) ``` ## 适配鸣潮的思路 1. 修改骨盆骨骼与脊椎骨骼相关逻辑。 1. FindAllBones_Recurse。 2. 增加BoneFilter,移除其他WeaponBone的影响。 3. 增加对Bip骨骼的适配逻辑。 1. g_spineName  = "spine_01" 2. parent.name == "pelvis" # 其他提取鸣潮资源与Renderdoc截帧方法 ![[提取鸣潮资产与截帧.png]]