原文来自于:http://www.gpbeta.com/post/develop/dragon-nest-mod/
资源结构
游戏使用EG自行研发的方式压缩打包成,PAK格式,资源包内不少格式属于EG自行研发并在文件头部写上"Eternity Engine XXX(格式) File XX(版本号)"标识,现在暂时没有(或没发现)相关格式的编辑工具.因此目前提取游戏资源后能直接编辑的格式仅有OGG(音频),DDS(贴图)
文件格式
从PAK提取的文件来看,《龙之谷》这个游戏使用了不少自行开发的"非通用"未知格式,如要对游戏进行修改,就必须进行格式转换,然而目前尚没有对这些未知格式进行通常转换的软件,因此只能对这些格式先进行整理了:
后缀名 | 文件头标识 | 文件格式 |
.act | Eternity Action File | 动画索引 |
.ani | Eternity Engine Ani File | 骨骼动画 |
.cam | Eternity Engine Camera File | 摄像头配置(猜测) |
.dds | DDS | 贴图格式 |
.dmv | DN_CutSceneData | 过场动画(猜测) |
.eff | Eternity Engine Effect File | 特效(猜测) |
.env | 无 | 环境配置(猜测) |
.ext | 无 | 游戏配置 |
.fontset | 无 | 字体配置 |
.lua | 无 | 游戏脚本(猜测) |
.mah | Eternity Engine Mesh File (0.12) | 模型格式(猜测) |
.msh | Eternity Engine Mesh File (0.1) | 模型和骨骼 |
.nav | 无 | |
.ogg | OggS | 音频格式 |
.ptc | Eternity Engine Particle File | 特殊文件(猜测) |
.skn | Eternity Engine Skin File | 贴图骨骼索引 |
.xml | 无 | 扩展标记 |
.ini | 无 | 信息配置 |
无 | Eternity World Infomation File |
其中主要的是.ani 骨骼动画 .act 动画索引 .dds 贴图 .skn 贴图估个索引 .msh 模型和骨骼
通过看原作者的描述,应该是先提取出msh模型然后转成其他模型再进maya处理,因为没有学过建模,都是我的推测。
这里存档原作者的软件避免原帖子丢失:
ExtEditor
龙之谷EXT二维表编辑器 是一款用于龙之谷二维表配置/索引文件(*.ext)便捷的编辑工具工具.使用方法请看压缩包里面的ReadMe.txt.
龙之谷EXT查看器2.0 :链接: https://pan.baidu.com/s/1d7lGHGjJHiWUztPg2C2zbg?pwd=47tq 提取码: 47tq
程序详细说明日志 :[龙之谷专题]二维表编辑器开发日志(支持最新DNT格式) | 热风CG工作室
MSHConverter
龙之谷MSH模型转换器是一款用于龙之谷模型文件(*.msh)与其他通用模型文件相互转的便捷工具.使用方法请看压缩包里面的ReadMe.txt.
龙之谷MSH模型转换器V1.2 :链接: https://pan.baidu.com/s/1qyuc61oIVi15szNZwgk5kQ?pwd=6644 提取码: 6644
程序详细说明日志 [龙之谷专题]MSH模型转换器开发日志 | 热风CG工作室
DNPacker
龙之谷PAK资源文件打包程序是一款用于龙之谷资源修改后的重新打包的一个非常方便的工具.使用方法请看压缩包里面的ReadMe.txt.
龙之谷PAK资源打包器V2.2 :链接: https://pan.baidu.com/s/1J6qMWevuxkanSdIMvZp1jA?pwd=n1qh 提取码: n1qh
DNModelBroswer
龙之谷模型查看器是由liuliqiang写的一个龙之谷模型查看程序,最新1.0a,新增OBJ导出功能.
龙之谷模型查看器V1.0a :链接: https://pan.baidu.com/s/1auUT08MGxIB2qyiPjpaUsA?pwd=7xix 提取码: 7xix
程序作者邮箱 点击下载
QuickBMS
QuickBMS是一个通用文件提取工具,支持使用脚本提取各种的资源包,前提是你必须知道资源包的结构.以下是用于提取《龙之谷》pak包的脚本,解压到QuickBMS目录下,运行QuickBMS并按照提示依次选择该脚本,pak文件,解压目录就可以提取游戏的"所有"资源了
QuickBMS下载页 :百度自己下载就可以
提取脚本(由某包提供) :链接: https://pan.baidu.com/s/1dX4XXxYIsRW5-J8wSi-Z5w?pwd=hdx9 提取码: hdx9
GameAssassin
GameAssassin(以下简称GA)是一个通用3D游戏资源截取工具,可以对运行中的游戏模型/骨骼(收费功能)/动画进行实时截取.
GameAssassin下载页 :自己百度下载就可以
需要注意的是,由于《龙之谷》使用了由盛大开发的GPK保护引擎,如果直接使用GA的"运行"方式打开dnlauncher.exe将会出现无法运行的问题.正确的用法是:先运行GA,取消软件的"激活"复选框,运行游戏,出现游戏主窗口后再点选"激活"复选框便可以正确抓取.
对于quickbms,一般用龙之谷补丁工坊抓取pak里面的模型等文件,比较傻瓜化。
龙之谷补丁工坊:链接: https://pan.baidu.com/s/1a6J6yGiooH0sanz2_-F6jA?pwd=42uc 提取码: 42uc
可能用到的:
pscc2019下载链接: https://pan.baidu.com/s/1o2itN5aIDLOZ46CovIz8kg?pwd=3jae 提取码: 3jae
ps2019注册机(GenP)下载链接: https://pan.baidu.com/s/1GCniJMC0NqOhT1azuqP_Sw?pwd=jxbq 提取码: jxbq
ps DDS插件: https://pan.baidu.com/s/12CWfl2zMRUUnaIBWNsN2HQ?pwd=ftcb 提取码: ftcb
autodesk maya 注册机:https://pan.baidu.com/s/1Y80IG_0qxSHicfzCCGC0-A?pwd=mrhp 提取码: mrhp
mesh转换器原作者讲解:
文件结构
MSH文件结构示意 | |
Header | 文件头部,用于索引各种数据 |
BoneData | 骨骼数据部分,大小由骨骼数量决定 |
MeshData | 模型数据部分,大小由Mesh数量决定 |
OtherData | 其他数据部分,大小由其他元素量决定 |
Header部分
Header结构示意 | ||||
name: 文件头标识 | ||||
name[256] | version: 文件版本 | |||
meshCount: Mesh总数 | ||||
version | meshCount | unknown | unknown | bbMax: BoundingBox最大值 |
bbMax | bbMin: BoundingBox最小值 | |||
bbMin | boneCount | unknown | boneCount: 骨骼总数 | |
otherCount | otherCount: 其他元素总数 |
struct Header{ char name[256]; //Eternity Engine Mesh File 0.1 int version; int meshCount; int unknown1; //0x1 int unknown2; //0x0 Vec3F bbMax; Vec3F bbMin; int boneCount; int unknown2; //0x1或0x0 int otherCount; };
BoneData部分
骨骼数据个数由Header索引,从文件头开始偏移0x400(1024)字节即为骨骼数据:
单个BoneData结构示意 | |
boneName[256] | boneName: 骨骼名称 |
m1 | m1: 骨骼变换矩阵第一行数据 |
m2 | m2: 骨骼变换矩阵第二行数据 |
m3 | m3: 骨骼变换矩阵第三行数据 |
m4 | m4: 骨骼变换矩阵第四行数据 |
struct BoneData{ char boneName[256]; //骨骼名称 Vec4F transformMatrix[4]; //骨骼变换矩阵 };
MeshData部分
MeshData部分由一个或多个Mesh数据组成,具体多少个Mesh由文件头部的meshCount决定.
每个Mesh数据由Mesh信息+Mesh数据组成,为了方便演示,示意图将把Mesh信息部分和Mesh数据部分合并一起表示,而具体实现代码将分成两个结构体:
单个MeshData结构示意 | ||||
sceneRoot[256] | sceneRoot: Mesh所属节点名称 | |||
meshName: Mesh名称 | ||||
meshName[256] | vertexCount: 顶点个数 | |||
indexCount: 顶点(三角面)索引数 | ||||
vertexCount | indexCount | unknown | renderMode | renderMode: 渲染模式,取值0x100/0x101 |
empty[512-16] | (GL_TRIANGLE_STRIP/GL_TRIANGLE) | |||
vertexIndex | vertexIndex: 顶点索引,indexCount个元素 | |||
vertexData | vertexData: 顶点数据,vertexCount个元素 | |||
normalData | normalData: 法线数据,vertexCount个元素 | |||
uvData | uvData: UV贴图数据,vertexCount个元素 | |||
weightIndex | weightIndex: 权重索引,vertexCoun个元素 | |||
weightData | weightData: 权重数据,vertexCount个元素 | |||
boneCount | boneCount: Mesh绑定骨骼个数 | |||
boneIndex | boneIndex: 骨骼索引,boneCount个元素 |
struct MeshInfo{ char sceneName[256]; char meshName[256]; int vertexCount; int indexCount; int unknown; int renderMode; char empty[512 - 16]; };
struct MeshDataPointer{//数据指针 char* pFaceIndex; //0x2(unsigned short) * indexCount char* pVertexData; //0x4(float) * 3 * vertexCount char* pNormalData; //0x4(float) * 3 * vertexCount char* pUVData; //0x4(float) * 2 * vertexCount char* pBoneIndex; //0x2(unsigned short) * 4 vertexCount char* pBoneWeight; //0x4(float) * 4 * vertexCount int* pBoneCount; //Mesh绑定的Bone数 char* pBoneName; //0x100(char [256]) * boneCount };
现在说一下MeshData里面各个元素的组成,虽然说规范和DirectX差不多,不过当时对DirectX的3D编程完全没有了解的我,可被这个搞得一头冒水呢……
MSH文件在保存浮点型数据时均使用单精度型(float)浮点数保存数据.
VertexData元素
VertexData,即顶点数据,是顶点在三维空间的x,y,z坐标数据.每个元素使用Vec3F结构保存数据.
VertexIndex元素
VertexIndex,即顶点索引,是程序画面的顺序数据,索引的是顶点数据.每个元素使用unsign short类型保存数据.
NormalData元素
NormalData,即法线数据,是每个顶点的法向数据,以三维空间坐标x,y,z表示.每个元素使用Vec3F结构保存数据.
UVData元素
UVData,即UV贴图数据,是每个顶点在二维贴图空间映射的u,v坐标.每个元素使用Vec2F结构保存数据.
WeightIndex元素
WeightIndex,即权重索引,是每个顶点的权重索引,每个顶点最多可以索引4个骨骼.每个元素使用4个unsign short类型保存数据.
WeightData
WeightData,即权重数据,是每个顶点的权重数据,每个顶点最多可以拥有4个骨骼权重.每个元素使用Vec4F结构保存数据.
BoneIndex
BoneIndex,即骨骼索引,用以给骨骼编序,只保存骨骼名称.每个元素使用char [256]字符型数组保存数据.
OtherData部分
暂时猜测该部分数据可能用于武器的"刀锋"特效.
自定义结构
class Vec2F{ public: float x; float y; Vec2F(){x = 0; y = 0;} Vec2F(KFbxVector2 &vector){ x = vector.GetAt(0); y = vector.GetAt(1);} Vec2F(KFbxVector4 &vector){ x = vector.GetAt(0); y = vector.GetAt(1);} };
class Vec3F : public Vec2F{ public: float z; Vec3F() : Vec2F(){z = 0;} Vec3F(KFbxVector4 &vector) : Vec2F(vector){ z = vector.GetAt(2);} };
class Vec4F : public Vec3F{ public: float w; Vec4F() : Vec3F(){w = 0;} Vec4F(KFbxVector4 &vector) : Vec3F(vector){ w = vector.GetAt(3);} };