796 lines
30 KiB
Markdown
796 lines
30 KiB
Markdown
# 导出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
|
||
|
||
## 绯红结系
|
||
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] <package> [<object> [<class>]]
|
||
umodel [command] [options] <directory>
|
||
umodel @<response_file>
|
||
|
||
<package> name of package to load - this could be a file name
|
||
with or without extension, or wildcard
|
||
<object> name of object to load
|
||
<class> class of object to load (useful, when trying to load
|
||
object with ambiguous name)
|
||
<directory> path to the game (see -path option)
|
||
|
||
Commands:
|
||
-view (default) visualize object; when no <object> 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 <package>)
|
||
-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=<set> 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
|
||
- 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
|
||
|
||
# 鸣潮
|
||
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
|
||
```
|
||
以秧秧为例:
|
||
- 模型位于:/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
|
||
- [x] Common
|
||
- [x] Levels
|
||
- [x] HeiHaiAn
|
||
- [x] HuangLong
|
||
- [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
|
||
# 碧蓝幻想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
|
||
# 破晓传说
|
||
- 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
|
||
|
||
|
||
# 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" |