Last active
April 18, 2023 15:57
-
-
Save Trass3r/914222adb44941e8013d1aa77bc7deba to your computer and use it in GitHub Desktop.
for %i in (*.kmf) do start /wait 010Editor -noui -nowarnings "-template:Dungeon Keeper 2 KMF.bt" "-script:Dungeon Keeper 2 KMF2OBJ.1sc" "%i"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Dungeon Keeper 2 Meshes (static and vertex animated) | |
local int i; | |
struct KMSH { | |
char magic[4]; | |
int size; // size of file incl. header | |
int version; // = 17 | |
struct KHEAD { | |
char magic[4]; | |
int size; | |
enum { | |
MESH = 1, | |
ANIM, | |
GROP | |
} format; | |
int uk; // = 1 | |
} header; | |
} kmsh; | |
if (kmsh.header.format > 2) | |
return; | |
local int isAnim = kmsh.header.format == 2; | |
struct Texture { | |
string name; | |
}; | |
struct MAT2 { | |
char magic[4]; | |
int size; | |
string name; | |
int numTextures; | |
struct { | |
string name; | |
} textures[numTextures]<optimize=false>; | |
enum MaterialFlag { | |
HAS_ALPHA = 0x0001, | |
// closely related to 0x1 but both exist in isolation too | |
UNKNOWN2 = 0x0002, // Some sort of shininess, used e.g. in axe blade, piranha tail | |
ALPHA_ADDITIVE = 0x0004, // Also emits "light" / glows..? | |
UNKNOWN8 = 0x0008, // ice? only set on #TRANS25#icey | |
UNKNOWN10 = 0x0010, // only used for 'Angel Bed RibsCreature Bed_Dark Angel2' | |
UNKNOWN20 = 0x0020, // never used? | |
IsGammaSet = 0x0040, // metal? sword, pickimpback etc. | |
IsBrightnessSet = 0x0080, // Some sort of glow? e.g. Lava | |
Invisible = 0x0100 // Environment mapped, invisible guys have this??? | |
} flags; | |
float brightness; | |
float gamma; | |
string envMap; | |
}; | |
struct MATL { | |
char magic[4]; | |
int size; | |
int numMat; | |
MAT2 materials[numMat]<optimize=false>; | |
} matl; | |
struct MESH { | |
char sig[4]; | |
int size; | |
struct HEAD { | |
char head[4]; | |
int size; | |
string meshname; | |
int numGroups; // number of SPRS groups | |
if (isAnim) { | |
int numFrames; | |
int numIndices; | |
} | |
int numVerts; // number of vertex positions defined in GEOM, worst case numFrames*numIndices | |
if (isAnim) { | |
enum {CLAMP, WRAP} frameFactorFunction; | |
} | |
float tx; // translation | |
float ty; | |
float tz; | |
if (isAnim) { | |
float cubeScale; | |
} | |
float scale; | |
int numLODs; // number of total LOD entries | |
} header; | |
struct CTRL { | |
char magic[4]; | |
int size; | |
int numCtrl; // only non-zero in ANIM files | |
struct { | |
short uk1; | |
short uk2; | |
int uk3; | |
} data[numCtrl]<optimize=false>; | |
} ctrl; | |
struct Model { | |
char magic[4]; | |
int size; | |
for (i=0; i < header.numGroups; i++) | |
{ | |
struct SPHD { | |
char magic[4]; | |
int size; | |
int numFacesPerLevel[header.numLODs]; // one entry for each LOD | |
int numVertsEx; // number of entries in the extended vertex list | |
float mmFactor; | |
} groups; | |
} | |
for (i=0; i < header.numGroups; i++) | |
{ | |
struct SPRS | |
{ | |
char magic[4]; | |
int size; | |
uint materialIdx; | |
if (isAnim) { | |
struct POLY { | |
char magic[4]; | |
int size; | |
} poly; | |
} | |
local int j; | |
for (j=0; j < header.numLODs; j++) | |
{ | |
struct { | |
struct { | |
ubyte x,y,z; // indices for the extended vertex list | |
} faces[groups[i].numFacesPerLevel[j]] <read=Str("(%u %u %u)",this.x,this.y,this.z)>; | |
} levels; | |
} | |
if (isAnim) { | |
char VERTmagic[4]; | |
int VERTsize; | |
} | |
struct VertEx { | |
if (!isAnim) ushort index; // index of the vertex in GEOM | |
ushort u; // up to 32768 | |
ushort v; | |
float nX; // the vertex normal | |
float nY; | |
float nZ; | |
if (isAnim) ushort itabIdx; | |
} vertsEx[groups[i].numVertsEx] <read=Str("%u, uv(%g, %g) n(%g,%g,%g)", isAnim ? this.itabIdx : 0, this.u / 32768.0, 1.0 - this.v / 32768.0, this.nX,this.nY,this.nZ)>; | |
} groupData; | |
} | |
} model; | |
if (isAnim) { | |
struct ITAB { | |
char magic[4]; | |
int size; | |
struct { | |
uint geomBase[header.numIndices]; | |
} frameChunks[(uint)((header.numFrames - 1) / 128.0 + 1)]; // per 128 frames | |
} itab; | |
typedef struct { | |
// 10 bits per coordinate (Z, Y, X) = 30 bits (2 last bits can be thrown away) | |
union { | |
int data; | |
struct { | |
uint z : 10 <format=hex>; | |
uint y : 10 <format=hex>; | |
uint x : 10 <format=hex>; | |
uint garbage : 2; | |
} bits; | |
} coords; | |
byte frameBase; | |
} GeomAnim<read=GeomAnimRead>; | |
struct ANIMGEOM { | |
char magic[4]; | |
int size; | |
// list of "keyframes" (positions) for each vertex | |
// "worst case" there is an entry for each frame | |
// there is at least an entry for the first and last frame | |
GeomAnim vertices[header.numVerts]; | |
} geom; | |
struct VGEO { | |
char magic[4]; | |
int size; | |
struct { | |
ubyte geomOffset[header.numFrames]; | |
} geomOffset[header.numIndices]; | |
} vgeo; | |
} | |
else { | |
struct GEOM { | |
char magic[4]; | |
int size; | |
struct Vec3f { | |
float x; | |
float y; | |
float z; | |
} vertices[header.numVerts] <read=Str("(%g, %g, %g)", this.x, this.y, this.z)>; | |
} geom; | |
} | |
} mesh; | |
string GeomAnimRead(GeomAnim& f) | |
{ | |
float x = (int)(((f.coords.data >> 20) & 1023) - 512) / 511.0f * mesh.header.scale; | |
float y = (int)(((f.coords.data >> 10) & 0x3ff) - 512) / 511.0f * mesh.header.scale; | |
float z = (int)(((f.coords.data >> 0) & 0x3ff) - 512) / 511.0f * mesh.header.scale; | |
string s; | |
SPrintf(s, "%u (%g %g %g)", f.frameBase, x, y, z); | |
return s; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// convert an open KMF to OBJ | |
if (kmsh.header.format > 2) | |
return; | |
uint numTotalVertexPositions = mesh.header.numVerts; | |
int computeAnimVertex(int frameIdx, int itabIdx) | |
{ | |
uint geomBase = mesh.itab.frameChunks[frameIdx / 128].geomBase[itabIdx]; | |
uint geomOffset = mesh.vgeo.geomOffset[itabIdx].geomOffset[frameIdx]; | |
uint geomIndex = geomBase + geomOffset; | |
byte frameBase = mesh.geom.vertices[geomIndex].frameBase; | |
Printf("grp %d tri %d: geomIndex %d base %d frame %d\n", i, j, geomIndex, mesh.geom.vertices[geomIndex].frameBase, frameIdx); | |
if ((frameIdx & 0x7F) > frameBase) { | |
byte nextFrameBase = mesh.geom.vertices[geomIndex+1].frameBase; | |
float geomFactor = (float) ((frameIdx & 0x7F) - frameBase) / (float) (nextFrameBase - frameBase); | |
Printf("nextBase %d geomFactor %g\n", nextFrameBase, geomFactor); | |
float x = (int)(mesh.geom.vertices[geomIndex].coords.bits.x - 512) / 511.0f * mesh.header.scale - mesh.header.tx; | |
float y = (int)(mesh.geom.vertices[geomIndex].coords.bits.y - 512) / 511.0f * mesh.header.scale - mesh.header.ty; | |
float z = (int)(mesh.geom.vertices[geomIndex].coords.bits.z - 512) / 511.0f * mesh.header.scale - mesh.header.tz; | |
x = x * (1 - geomFactor) + geomFactor * ((int)(mesh.geom.vertices[geomIndex+1].coords.bits.x - 512) / 511.0f * mesh.header.scale - mesh.header.tx); | |
y = y * (1 - geomFactor) + geomFactor * ((int)(mesh.geom.vertices[geomIndex+1].coords.bits.y - 512) / 511.0f * mesh.header.scale - mesh.header.ty); | |
z = z * (1 - geomFactor) + geomFactor * ((int)(mesh.geom.vertices[geomIndex+1].coords.bits.z - 512) / 511.0f * mesh.header.scale - mesh.header.tz); | |
FPrintf(objFile, "v %g %g %g\n", x, -z, y); | |
++numTotalVertexPositions; | |
return numTotalVertexPositions; | |
} | |
return geomIndex + 1; // OBJ indices are 1-based | |
} | |
int sourceFile = GetFileNum(); | |
const int isAnim = kmsh.header.format == 2; | |
int i, j; | |
int NumFaces = 0; | |
for (i = 0; i < mesh.header.numGroups; i++) | |
NumFaces += mesh.model.groups[i].numFacesPerLevel[0]; | |
Printf("mesh numGroups: %d numVerts: %d numfaces: %d\n", mesh.header.numGroups, mesh.header.numVerts, NumFaces); | |
int materialFile = FileNew("Text", false); | |
for (i = 0; i < matl.numMat; ++i) { | |
FPrintf(materialFile, "newmtl %s\n", matl.materials[i].name); | |
FPrintf(materialFile, "map_kd %s.png\n", matl.materials[i].textures[0].name); | |
FPrintf(materialFile, "map_bump %s_n.png\n", matl.materials[i].textures[0].name); | |
} | |
string sourceFilename = GetFileName(); | |
FileSelect(materialFile); | |
FileSave(FileNameSetExtension(sourceFilename, ".mtl")); | |
FileClose(); | |
FileSelect(sourceFile); | |
int objFile = FileNew("Text", false); | |
FPrintf(objFile, "mtllib %s\n", FileNameGetBase(FileNameSetExtension(sourceFilename, ".mtl"))); | |
FPrintf(objFile, "# %d vertices\n", mesh.header.numVerts); | |
// write the vertex data | |
for (i = 0; i < mesh.header.numVerts; i++) | |
{ | |
if (isAnim) | |
FPrintf(objFile, "v %g %g %g\n", | |
(int)(mesh.geom.vertices[i].coords.bits.x - 512) / 511.0f * mesh.header.scale - mesh.header.tx, | |
-((int)(mesh.geom.vertices[i].coords.bits.z - 512) / 511.0f * mesh.header.scale - mesh.header.tz), | |
(int)(mesh.geom.vertices[i].coords.bits.y - 512) / 511.0f * mesh.header.scale - mesh.header.ty); | |
else | |
FPrintf(objFile, "v %g %g %g\n", | |
mesh.geom.vertices[i].x - mesh.header.tx, // TODO: * mesh.header.scale ? | |
-(mesh.geom.vertices[i].z - mesh.header.tz), | |
mesh.geom.vertices[i].y - mesh.header.ty); | |
} | |
// write the normals and UVs | |
for (i = 0; i < mesh.header.numGroups; i++) | |
{ | |
FPrintf(objFile, "# %s %d uniq verts\n", matl.materials[mesh.model.groupData[i].materialIdx].name, mesh.model.groups[i].numVertsEx); | |
for (j=0; j < mesh.model.groups[i].numVertsEx; ++j) | |
{ | |
#define curVertEx mesh.model.groupData[i].vertsEx[j] | |
FPrintf(objFile, "vt %g %g\n", curVertEx.u / 32768.0, 1.0 - curVertEx.v / 32768.0); | |
//FPrintf(objFile, "vn %g %g %g\n", curVertEx.nX, -curVertEx.nZ, curVertEx.nY); | |
} | |
} | |
if (isAnim) | |
FPrintf(objFile, "# %d frames\n", mesh.header.numFrames); | |
int v1idx, v2idx, v3idx; | |
int countVertExs; | |
int frameIdx = 0; | |
const int lodLevel = 0; | |
for (frameIdx = 0; frameIdx < (isAnim ? mesh.header.numFrames : 1); ++frameIdx) | |
{ | |
FPrintf(objFile, "o %s_%d\n\n", /*mesh.header.meshname is often nonsense*/ FileNameGetBase(sourceFilename, false), frameIdx); | |
countVertExs = 0; | |
// loop through all groups | |
for (i=0; i < mesh.header.numGroups; i++) | |
{ | |
FPrintf(objFile, "g %s_%d\n", | |
matl.materials[mesh.model.groupData[i].materialIdx].name, frameIdx); | |
//mesh.header.meshname, i, frameIdx); | |
FPrintf(objFile, "usemtl %s\n", matl.materials[mesh.model.groupData[i].materialIdx].name); | |
// loop through all faces in the selected LOD | |
FPrintf(objFile, "# %d faces\n", mesh.model.groups[i].numFacesPerLevel[lodLevel]); | |
for (j=0; j < mesh.model.groups[i].numFacesPerLevel[lodLevel]; ++j) | |
{ | |
#define v1 mesh.model.groupData[i].vertsEx[mesh.model.groupData[i].levels[lodLevel].faces[j].x] | |
#define v2 mesh.model.groupData[i].vertsEx[mesh.model.groupData[i].levels[lodLevel].faces[j].y] | |
#define v3 mesh.model.groupData[i].vertsEx[mesh.model.groupData[i].levels[lodLevel].faces[j].z] | |
if (isAnim) { | |
v1idx = computeAnimVertex(frameIdx, v1.itabIdx); | |
v2idx = computeAnimVertex(frameIdx, v2.itabIdx); | |
v3idx = computeAnimVertex(frameIdx, v3.itabIdx); | |
} else { | |
v1idx = v1.index + 1; | |
v2idx = v2.index + 1; | |
v3idx = v3.index + 1; | |
} | |
FPrintf(objFile, "f %d/%d %d/%d %d/%d\n", // %d/%d/%d %d/%d/%d %d/%d/%d | |
v1idx, | |
//countVertExs + mesh.model.groupData[i].levels[lodLevel].faces[j].x+1, | |
countVertExs + mesh.model.groupData[i].levels[lodLevel].faces[j].x+1, | |
v3idx, // invert vertex order to get correct backface culling | |
//countVertExs + mesh.model.groupData[i].levels[lodLevel].faces[j].z+1, | |
countVertExs + mesh.model.groupData[i].levels[lodLevel].faces[j].z+1, | |
v2idx, | |
//countVertExs + mesh.model.groupData[i].levels[lodLevel].faces[j].y+1, | |
countVertExs + mesh.model.groupData[i].levels[lodLevel].faces[j].y+1 | |
); | |
} | |
countVertExs += mesh.model.groups[i].numVertsEx; | |
} | |
} | |
FileSelect(objFile); | |
FileSave(FileNameSetExtension(sourceFilename, ".obj")); | |
FileClose(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if (kmsh.header.format > 2) | |
return; | |
int sourceFile = GetFileNum(); | |
string sourceFilePath = GetFileName(); | |
string sourceFileName = FileNameGetBase(sourceFilePath, true); | |
int logFile = FileOpen("log.txt", false, "Text"); | |
//int logFile = FileNew("Text", false); | |
FSeek(FileSize()); | |
FileSelect(sourceFile); | |
const int isAnim = kmsh.header.format == 2; | |
int i, j; | |
int NumFaces = 0; | |
for (i = 0; i < mesh.header.numGroups; i++) | |
NumFaces += mesh.model.groups[i].numFacesPerLevel[0]; | |
FPrintf(logFile, "mesh %s: \"%s\"\n", sourceFileName, mesh.header.meshname); | |
FPrintf(logFile, "numGroups: %d numVerts: %d numFaces: %d numLODs: %u numFrames: %d numIndices: %d\n", | |
mesh.header.numGroups, | |
mesh.header.numVerts, | |
NumFaces, | |
mesh.header.numLODs, | |
isAnim ? mesh.header.numFrames : 1, | |
isAnim ? mesh.header.numIndices : 1); | |
for (i = 0; i < matl.numMat; ++i) { | |
FPrintf(logFile, "mtl %s\n", matl.materials[i].name); | |
for (j = 0; j < matl.materials[i].numTextures; ++j) | |
FPrintf(logFile, " tex: %s\n", matl.materials[i].textures[j].name); | |
if (matl.materials[i].flags) | |
FPrintf(logFile, " flags: 0x%X\n", matl.materials[i].flags); | |
if (matl.materials[i].brightness) | |
FPrintf(logFile, " brightness: %g\n", matl.materials[i].brightness); | |
if (matl.materials[i].gamma) | |
FPrintf(logFile, " gamma: %g\n", matl.materials[i].gamma); | |
if (matl.materials[i].envMap != "DefaultEnvMap") | |
FPrintf(logFile, " envMap: %s\n", matl.materials[i].envMap); | |
} | |
FPrintf(logFile, "Groups:\n"); | |
for (i=0; i < mesh.header.numGroups; i++) | |
{ | |
FPrintf(logFile, "vertsEx %d mat %d mm %g\n", | |
mesh.model.groups[i].numVertsEx, | |
mesh.model.groupData[i].materialIdx, | |
mesh.model.groups[i].mmFactor | |
); | |
for (j=0; j < mesh.header.numLODs; ++j) | |
FPrintf(logFile, " %d", mesh.model.groups[i].numFacesPerLevel[j]); | |
FPrintf(logFile, "\n"); | |
} | |
FileSelect(logFile); | |
FileSave(); | |
FileClose(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment