永继研究室

文档-资料-开源

英雄联盟SKN文件结构

文件采用小端(little-endian)字节序

int32

文件类型标识,没有扩展名时可以判断文件类型 

int16

Material数量

int16

Obj数量(不知道什么作用)

char[28]*Material数量

Material数据

int[32]

序号

char[64]

名称

int[32]

开始的顶点位置

int[32]

拥有的顶点数量

int[32]

开始的顶点索引位置

int[32]

拥有的顶点索引数量

int32

顶点索引数量

int32

顶点数量

int16*顶点索引数量

顶点索引,每3个顶点组成一个面

char[52]*顶点数量

顶点数据

float*3

顶点位置: x,y,z

char[4]

4个骨骼索引,每个索引用一个char表示(这里还有疑问,骨骼索引可能还有算法)

float*4

对应4个骨骼索引的权重

float*3

顶点的法线

float*2

顶点的UV坐标


下面给出一段命令行C++工具代码

/*
League of Legends Custom Skin Tool-Kit
by Jahrain
This is a simple tool that can be used to compile and decompile .skn and .skl files
in order to create custom skin models for League of Legends
*/
#include
#include
#include
#include
using namespace std;

struct Header {
//Structure for header on skl files
char fileType[8];
int numObjects;
int skeletonHash;
int numElements;
};

struct Bone {
//Structure for a bone in skl files
char name[32];
int parent;
float scale;
float matrix[3][4];
};

struct SkinModelHeader {
//Structure for the Header in skn files
int magic;
short numMaterials;
short numObjects;
};
struct SkinModelMaterial {
//Structure for a material block in skn files
int matIndex;
char name[64];
int startVertex;
int numVertices;
int startIndex;
int numIndices;
};
struct SkinModelVertex {
//Vertex block in skn files
float position[3];
char boneIndex[4];
float weights[4];
float normal[3];
float texcoords[2];
};
struct SkinModelData {
//data block in skn files
int numIndices;
int numVertices;
vector indices;
vector verteces;
};

struct SkinModel {
//Skin model data structure
SkinModelHeader header;
vector materials;
SkinModelData modelData;

void exportObj(string fileName) {
//exports this skin model as a .obj file
ofstream fout(fileName.c_str());
for (int i = 0; i < modelData.verteces.size(); i++) {
fout << "v " << modelData.verteces[i].position[0] << " " << modelData.verteces[i].position[1] << " " << modelData.verteces[i].position[2] << endl;
fout << "vn " << modelData.verteces[i].normal[0] << " " << modelData.verteces[i].normal[1] << " " << modelData.verteces[i].normal[2] << endl;
fout << "vt " << modelData.verteces[i].texcoords[0] << " " << 1-modelData.verteces[i].texcoords[1] << endl;
}
if (materials.size()) {
fout << "g mat_" << materials[0].name << endl;
}
for (int i = 0; i < modelData.numIndices/3; i++) {
int a = modelData.indices[i*3] + 1;
int b = modelData.indices[i*3 + 1] + 1;
int c = modelData.indices[i*3 + 2] + 1;
fout << "f " << a << '/' << a << '/' << a << " " << b << '/' << b << '/' << b << " " << c << '/' << c << '/' << c << endl;
}
}

void exportSkn(string fileName) {
//export this SkinModel as .skn which can be used by league of legends
ofstream fout;
fout.open(fileName.c_str(), ios::binary);
fout.write((char*)(void*)&header,sizeof(SkinModelHeader));
for (int i = 0; i < header.numMaterials; i++) {
fout.write((char*)(void*)&materials[i], sizeof(SkinModelMaterial));
}
fout.write((char*)(void*)&modelData.numIndices,4);
fout.write((char*)(void*)&modelData.numVertices,4);
for (int i = 0; i < modelData.numIndices; i++) {
fout.write((char*)(void*)&modelData.indices[i], 2);
}
for (int i = 0; i < modelData.numVertices; i++) {
fout.write((char*)(void*)&modelData.verteces[i],sizeof(SkinModelVertex));
}
}

void importSkn(string infile) {
//import from .skn file
ifstream fin;
fin.open(infile.c_str(), ios::binary);
if (fin.fail()) {
cout << "ERROR: could not open " << infile << endl;
exit(1);
}
fin.read((char*)(void*)&header,sizeof(SkinModelHeader));

for (int i = 0; i < header.numMaterials; i++) {
SkinModelMaterial mat;
fin.read((char*)(void*)&mat, sizeof(SkinModelMaterial));
materials.push_back(mat);
}

fin.read((char*)(void*)&modelData.numIndices,4);
fin.read((char*)(void*)&modelData.numVertices,4);

for (int i = 0; i < modelData.numIndices; i++) {
short idx;
fin.read((char*)(void*)&idx, 2);
modelData.indices.push_back(idx);
}
for (int i = 0; i < modelData.numVertices; i++) {
SkinModelVertex vtx;
fin.read((char*)(void*)&vtx,sizeof(SkinModelVertex));
modelData.verteces.push_back(vtx);
}
}

void importMesh(string infile) {
//import from ASCII mesh output from Maya Script
//cout << "Compiling skin file: " << infile << " to binary file: " << outfile << endl;
ifstream fin(infile.c_str());
if (fin.fail()) {
cout << "ERROR: could not open " << infile << endl; exit(1); } int numVerts; fin >> numVerts;
modelData.numVertices = numVerts;
for (int i = 0; i < numVerts; i++) { SkinModelVertex vtx; fin >> vtx.position[0] >> vtx.position[1] >> vtx.position[2];

for (int j = 0; j < 3; j++) { int boneIdx = 0; string boneIdxStr; fin >> boneIdxStr;
if (boneIdxStr != "#NULL") {
string tmp;
for (int k = boneIdxStr.length() - 1; k >= 0; k--) {
if (boneIdxStr[k] == '_') {
boneIdxStr[k] = ' ';
break;
}
}
}
char name[32];
sscanf(boneIdxStr.c_str(), "%s %d",name,&boneIdx);
vtx.boneIndex[j] = boneIdx;

}
vtx.boneIndex[3] = 0;
fin >> vtx.weights[0] >> vtx.weights[1] >> vtx.weights[2];
vtx.weights[3] = 0;
//normalize weights
float weightSum = vtx.weights[0] + vtx.weights[1] + vtx.weights[2];
vtx.weights[0] *= 1.0f/weightSum;
vtx.weights[1] *= 1.0f/weightSum;
vtx.weights[2] *= 1.0f/weightSum;
fin >> vtx.normal[0] >> vtx.normal[1] >> vtx.normal[2];
fin >> vtx.texcoords[0] >> vtx.texcoords[1];
vtx.texcoords[1] = 1 - vtx.texcoords[1]; //flip the UVs
modelData.verteces.push_back(vtx);
}
SkinModelMaterial mat;
memset((void*)&mat,0,sizeof(SkinModelMaterial));
fin >> mat.name;
int numFaces;
fin >> numFaces;
string nameStr = string(mat.name);
if (nameStr != "lambert1") {
cout << "NOTE: The material name is " << nameStr << ". Make sure this matches the same material as the original skin file!!!" << endl;
header.numMaterials = 1;
mat.matIndex = 1;
mat.numIndices = numFaces*3;
mat.numVertices = numVerts;
mat.startIndex = 0;
mat.startVertex = 0;
materials.push_back(mat);
} else {
cout << "WARNING: The default material was used because this model used the default material lambert1. Only do this if the original .skn file has no materials." << endl;
header.numMaterials = 0;
}
header.magic = 1122867;
header.numObjects = 1;
modelData.numIndices = numFaces*3;
for (int i = 0; i < numFaces; i++) {
for (int j = 0; j < 3; j++) { short face; fin >> face;
modelData.indices.push_back(face);
}
}
}

};

void dumpSkeleton(string infile, string outfile) {
//dump out the contents of a .skl file as ASCII for importing into Maya or some other 3d Application.
ifstream fin;
fin.open(infile.c_str(), ios::binary);
if (fin.fail()) {
cout << "ERROR: could not open " << infile << endl;
exit(1);
}
ofstream fout(outfile.c_str());
Header header;
fin.read((char*)(void*)&header, sizeof(header));
cout << "Found " << header.numElements << " bones" << endl;
fout << header.fileType << endl;
fout << header.numObjects << endl;
fout << header.skeletonHash << endl;
fout << header.numElements << endl;
for (int i = 0; i < header.numElements; i++) {
Bone bone;
fin.read((char*)(void*)&bone, sizeof(Bone));
fout << bone.name << endl;
fout << bone.parent << endl;
fout << bone.scale << endl;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
fout << bone.matrix[i][j] << " ";
}
fout << endl;
}
}
}

void compileSkeleton(string infile, string outfile) {
//Compile the ASCII skeleton file back into a .skl file
ifstream fin;
fin.open(infile.c_str());
if (fin.fail()) {
cout << "ERROR: could not open " << infile << endl; exit(1); } ofstream fout; fout.open(outfile.c_str(), ios::binary); Header header; fin >> header.fileType;
fin >> header.numObjects;
fin >> header.skeletonHash;
fin >> header.numElements;

fout.write((const char*)&header,sizeof(Header));
for (int i = 0; i < header.numElements; i++) { Bone bone; memset(bone.name,0,32); fin >> bone.name;
fin >> bone.parent;
fin >> bone.scale;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) { fin >> bone.matrix[i][j];
}
}
fout.write((const char*)&bone, sizeof(Bone));
}
}

int main(int argc, char *argv[]) {

if (argc != 5) {
cout &lt;&lt; "Usage: lolskintool <command></command> " &lt;&lt; endl;
cout &lt;&lt; "Valid commands are: c (compile), d (decompile)" &lt;&lt; endl;
cout &lt;&lt; "Supported types are: skl (skeleton), skn (skin)" &lt;&lt; endl;
return 2;
}
string cmd = string(argv[1]);
string type = string(argv[2]);
string inputFile = string(argv[3]);
string outputFile = string(argv[4]);

if (cmd == "compile" || cmd == "c") {
if (type == "skl" || type == "skeleton") {
cout &lt;&lt; "Compiling ASCII skeleton file: " &lt;&lt; inputFile &lt;&lt; " to binary .skl file: " &lt;&lt; outputFile &lt;&lt; endl;
compileSkeleton(inputFile, outputFile);
cout &lt;&lt; "Done!" &lt;&lt; endl;
} else if (type == "skn" || type == "skin") {
SkinModel model;
cout &lt;&lt; "Compiling ASCII skin mesh file: " &lt;&lt; inputFile &lt;&lt; " to binary .skn file: " &lt;&lt; outputFile &lt;&lt; endl;
model.importMesh(inputFile);
model.exportSkn(outputFile);
cout &lt;&lt; "Done!" &lt;&lt; endl;
} else {
cout &lt;&lt; "Unsupported file type" &lt;&lt; endl;
return 2;
}
} else if (cmd == "decompile" || cmd == "d") {
if (type == "skl" || type == "skeleton") {
cout &lt;&lt; "Decompiling binary .skl file: " &lt;&lt; inputFile &lt;&lt; " to ASCII file: " &lt;&lt; outputFile &lt;&lt; endl;
dumpSkeleton(inputFile, outputFile);
cout &lt;&lt; "Done!" &lt;&lt; endl;
} else if (type == "skn" || type == "skin") {
cout &lt;&lt; "Decompiling binary .skn file: " &lt;&lt; inputFile &lt;&lt; " to .obj file: " &lt;&lt; outputFile &lt;&lt; endl;
SkinModel model;
model.importSkn(inputFile);
model.exportObj(outputFile);
cout &lt;&lt; "Done!" &lt;&lt; endl;
} else {
cout &lt;&lt; "Unsupported file type" &lt;&lt; endl;
return 2;
}
} else {
cout &lt;&lt; "Unsupported command" &lt;&lt; endl;
return 2;
}
return 0;
}

, , ,

发表评论