From c6ce69540623c827c9977131026378c699ba5592 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Fri, 17 Feb 2023 23:48:08 +0200 Subject: [PATCH] Add dkm models support (daikatana) Based on: * [TrenchBroom](https://github.com/TrenchBroom/TrenchBroom/blob/master/common/src/IO/DkmParser.cpp) * [dkm2md2](http://charles.hollemeersch.net/daikatana-tools/) --- README.md | 10 +- src/common/header/files.h | 68 +++++++-- src/files/models.c | 289 ++++++++++++++++++++++++++++++++++++-- src/vk/vk_model.c | 2 + 4 files changed, 337 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index e0f5160..0b8bba0 100644 --- a/README.md +++ b/README.md @@ -128,8 +128,8 @@ windows. ### Custom model format support -Render unofficially supports mdl/Quake 1 and fm/Heretic2, are provided -without any warranty of support. The simplest way to check is renaming the -mdl/fm format file to md2 and place instead the original md2 file. FM is -rendered with all meshes without support of filtering/selecting the exact -part of the model. +Render unofficially supports mdl/Quake 1, dkm/Daikatana and fm/Heretic2, +are provided without any warranty of support. The simplest way to check +is renaming the mdl/dkm/fm format file to md2 and place instead the original +tris.md2 file. FM is rendered with all meshes without support of +filtering/selecting the exact part of the model. diff --git a/src/common/header/files.h b/src/common/header/files.h index c6e5c0f..b1afaab 100644 --- a/src/common/header/files.h +++ b/src/common/header/files.h @@ -71,22 +71,22 @@ typedef struct #define MDL_VERSION 6 /* Texture coords */ -struct mdl_texcoord_t +typedef struct mdl_texcoord_s { int onseam; int s; int t; -}; +} mdl_texcoord_t; /* Triangle info */ -struct mdl_triangle_t +typedef struct mdl_triangle_s { int facesfront; /* 0 = backface, 1 = frontface */ int vertex[3]; /* vertex indices */ -}; +} mdl_triangle_t; /* MDL header */ -typedef struct mdl_header_t +typedef struct mdl_header_s { int ident; /* magic number: "IDPO" */ int version; /* version: 6 */ @@ -107,7 +107,7 @@ typedef struct mdl_header_t int synctype; /* 0 = synchron, 1 = random */ int flags; /* state flag */ float size; /* average size of triangles */ -} dmdlo_t; +} mdl_header_t; /* .MD2 triangle model file format */ @@ -186,22 +186,60 @@ typedef struct /* .FM triangle model file format */ -#define RAVENFMHEADER (('d'<<24)+('a'<<16)+('e'<<8)+'h') +#define RAVENFMHEADER (('d' << 24) + ('a' << 16) + ('e' << 8) + 'h') -typedef struct +typedef struct fmheader_s { - int skinwidth; - int skinheight; - int framesize; /* byte size of each frame */ + int skinwidth; + int skinheight; + int framesize; // byte size of each frame + + int num_skins; + int num_xyz; + int num_st; // greater than num_xyz for seams + int num_tris; + int num_glcmds; // dwords in strip/fan command list + int num_frames; + int num_mesh_nodes; +} fmheader_t; + +/* Daikatana dkm format */ +#define DKMHEADER (('D' << 24) + ('M' << 16) + ('K' << 8) + 'D') + +#define DKM1_VERSION 1 +#define DKM2_VERSION 2 + +typedef struct dkmtriangle_s +{ + short extra; /* no idea */ + short num_uvframes; /* no idea */ + short index_xyz[3]; + short index_st[3]; +} dkmtriangle_t; + +typedef struct dkm_header_s +{ + int ident; /* magic number: "DKMD" */ + int version; /* version: 1 or 2 */ + vec3_t translate; /* translation vector */ + int framesize; /* byte size of each frame */ int num_skins; int num_xyz; - int num_st; /* greater than num_xyz for seams */ + int num_st; /* greater than num_xyz for seams */ int num_tris; - int num_glcmds; /* dwords in strip/fan command list */ + int num_glcmds; /* dwords in strip/fan command list */ int num_frames; - int num_mesh_nodes; -} fmheader_t; + int num_surf; /* no idea */ + + int ofs_skins; /* each skin is a MAX_SKINNAME string */ + int ofs_st; /* byte offset from start for stverts */ + int ofs_tris; /* offset for dtriangles */ + int ofs_frames; /* offset for first frame */ + int ofs_glcmds; + int ofs_surf; /* no idea */ + int ofs_end; /* end of file */ +} dkm_header_t; /* .SP2 sprite file format */ diff --git a/src/files/models.c b/src/files/models.c index 78e807f..5238dfc 100644 --- a/src/files/models.c +++ b/src/files/models.c @@ -82,7 +82,7 @@ Load the frames ================= */ static void -Mod_LoadFrames (dmdl_t *pheader, byte *src) +Mod_LoadFrames (dmdl_t *pheader, byte *src, vec3_t translate) { int i; @@ -100,6 +100,7 @@ Mod_LoadFrames (dmdl_t *pheader, byte *src) { poutframe->scale[j] = LittleFloat (pinframe->scale[j]); poutframe->translate[j] = LittleFloat (pinframe->translate[j]); + poutframe->translate[j] += translate[j]; } // verts are all 8 bit, so no swapping needed memcpy (poutframe->verts, pinframe->verts, @@ -134,6 +135,139 @@ Mod_LoadDTriangleList (dmdl_t *pheader, dtriangle_t *pintri) } } +/* +================= +Mod_LoadDTriangleList + +Load DKM triangle lists +================= +*/ +static void +Mod_LoadDkmTriangleList (dmdl_t *pheader, dkmtriangle_t *pintri) +{ + dtriangle_t *pouttri; + int i; + + pouttri = (dtriangle_t *) ((char *)pheader + pheader->ofs_tris); + + for (i=0 ; inum_tris ; i++) + { + int j; + + for (j=0 ; j<3 ; j++) + { + pouttri[i].index_xyz[j] = LittleShort (pintri[i].index_xyz[j]); + pouttri[i].index_st[j] = LittleShort (pintri[i].index_st[j]); + } + } +} + +/* +================= +Mod_LoadDKMCmdList + +Load the DKM glcmds +================= +*/ +static void +Mod_LoadDKMCmdList (const char *mod_name, dmdl_t *pheader, int *pincmd) +{ + int *poutcmd, *pendcmd; + int i; + + poutcmd = (int *)((char*)pheader + pheader->ofs_glcmds); + pendcmd = poutcmd + pheader->num_glcmds; + /* read command count */ + i = LittleLong(*pincmd++); + *poutcmd++ = i; + + while (i != 0) + { + if (i < 0) + { + i = -i; + } + + /* skip unused surf_id and skin index */ + pincmd += 2; + + while (i) + { + poutcmd[0] = LittleLong (pincmd[1]); + poutcmd[1] = LittleLong (pincmd[2]); + poutcmd[2] = LittleLong (pincmd[0]); + poutcmd += 3; + pincmd += 3; + i --; + } + + /* read command count */ + i = LittleLong(*pincmd++); + *poutcmd++ = i; + + if (pendcmd < poutcmd) + { + R_Printf(PRINT_ALL, "%s: Entity %s has possible broken glcmd.\n", + __func__, mod_name); + break; + } + } + memset (poutcmd, 0, (pendcmd - poutcmd) * sizeof(int)); +} + +/* +================= +Mod_DkmLoadFrames + +Load the Dkm v2 frames +================= +*/ +static void +Mod_LoadDkmFrames (dmdl_t *pheader, const byte *src, size_t infamesize, vec3_t translate) +{ + int i; + + for (i=0 ; inum_frames ; i++) + { + daliasframe_t *pinframe, *poutframe; + dtrivertx_t *outverts; + byte *inverts; + int j; + + pinframe = (daliasframe_t *) (src + i * infamesize); + poutframe = (daliasframe_t *) ((byte *)pheader + + pheader->ofs_frames + i * pheader->framesize); + + memcpy (poutframe->name, pinframe->name, sizeof(poutframe->name)); + for (j=0 ; j<3 ; j++) + { + poutframe->scale[j] = LittleFloat (pinframe->scale[j]); + poutframe->translate[j] = LittleFloat (pinframe->translate[j]); + poutframe->translate[j] += translate[j]; + } + + poutframe->scale[0] *= 8; + poutframe->scale[1] *= 4; + poutframe->scale[2] *= 8; + + inverts = (byte *)&pinframe->verts; + outverts = poutframe->verts; + /* dkm vert version 2 has unalligned by int size struct */ + for(j=0; j < pheader->num_xyz; j++) + { + int xyz; + + xyz = LittleLong (*((int *)inverts)); + outverts[j].v[0] = ((xyz & 0xFFE00000) >> (21 + 3)) & 0xFF; + outverts[j].v[1] = ((xyz & 0x1FF800) >> (11 + 2)) & 0xFF; + outverts[j].v[2] = ((xyz & 0x7FF) >> 3) & 0xFF; + inverts += sizeof(int); + outverts[j].lightnormalindex = *inverts; + inverts ++; + } + } +} + /* ================= Mod_LoadMDL @@ -144,7 +278,7 @@ Mod_LoadMDL (const char *mod_name, const void *buffer, int modfilelen, vec3_t mins, vec3_t maxs, struct image_s **skins, findimage_t find_image, modtype_t *type) { - const dmdlo_t *pinmodel; + const mdl_header_t *pinmodel; int version; dmdl_t *pheader; void *extradata; @@ -154,7 +288,7 @@ Mod_LoadMDL (const char *mod_name, const void *buffer, int modfilelen, int num_skins, num_xyz, num_st, num_tris, num_glcmds, num_frames; int ofs_skins, ofs_st, ofs_tris, ofs_frames, ofs_glcmds, ofs_end; - pinmodel = (dmdlo_t *)buffer; + pinmodel = (mdl_header_t *)buffer; version = LittleLong (pinmodel->version); if (version != MDL_VERSION) @@ -264,10 +398,10 @@ Mod_LoadMDL (const char *mod_name, const void *buffer, int modfilelen, int i; const byte *curr_pos; - struct mdl_triangle_t *triangles; - struct mdl_texcoord_t *texcoords; + mdl_triangle_t *triangles; + mdl_texcoord_t *texcoords; - curr_pos = (byte*)buffer + sizeof (struct mdl_header_t); + curr_pos = (byte*)buffer + sizeof (mdl_header_t); // register all skins for (i = 0; i < num_skins; ++i) @@ -298,8 +432,8 @@ Mod_LoadMDL (const char *mod_name, const void *buffer, int modfilelen, { dstvert_t *poutst = (dstvert_t *) ((byte *)pheader + ofs_st); - texcoords = (struct mdl_texcoord_t *)curr_pos; - curr_pos += sizeof (struct mdl_texcoord_t) * num_st; + texcoords = (mdl_texcoord_t *)curr_pos; + curr_pos += sizeof (mdl_texcoord_t) * num_st; for(i = 0; i < num_st; i++) { @@ -322,8 +456,8 @@ Mod_LoadMDL (const char *mod_name, const void *buffer, int modfilelen, { dtriangle_t *pouttri = (dtriangle_t *) ((byte *)pheader + ofs_tris); - triangles = (struct mdl_triangle_t *) curr_pos; - curr_pos += sizeof (struct mdl_triangle_t) * num_tris; + triangles = (mdl_triangle_t *) curr_pos; + curr_pos += sizeof (mdl_triangle_t) * num_tris; for (i=0 ; iofs_frames); + Mod_LoadFrames (pheader, (byte *)pinmodel + pheader->ofs_frames, translate); // // load the glcmds @@ -743,6 +878,8 @@ Mod_LoadFlexModel(const char *mod_name, const void *buffer, int modfilelen, } else if (Q_strncasecmp(blockname, "frames", sizeof(blockname)) == 0) { + vec3_t translate = {0, 0, 0}; + if (version != 1) { R_Printf(PRINT_ALL, "%s: Invalid %s version %d", @@ -756,7 +893,7 @@ Mod_LoadFlexModel(const char *mod_name, const void *buffer, int modfilelen, return NULL; } - Mod_LoadFrames (pheader, (byte *)src); + Mod_LoadFrames (pheader, (byte *)src, translate); } else if (Q_strncasecmp(blockname, "glcmds", sizeof(blockname)) == 0) { @@ -854,6 +991,121 @@ Mod_LoadFlexModel(const char *mod_name, const void *buffer, int modfilelen, return extradata; } +static void * +Mod_LoadDKMModel(const char *mod_name, const void *buffer, int modfilelen, + vec3_t mins, vec3_t maxs, struct image_s **skins, findimage_t find_image, + modtype_t *type) +{ + dmdl_t dmdlheader, *pheader; + dkm_header_t header; + void *extradata; + int i; + + if (sizeof(dkm_header_t) > modfilelen) + { + R_Printf(PRINT_ALL, "%s: model %s file size(%d) too small", + __func__, mod_name, modfilelen); + } + + // byte swap the header fields and sanity check + for (i=0 ; i modfilelen) + { + R_Printf(PRINT_ALL, "%s: model %s file size(%d) too small, should be %d", + __func__, mod_name, modfilelen, header.ofs_end); + return NULL; + } + + /* copy back all values */ + dmdlheader.ident = IDALIASHEADER; + dmdlheader.version = ALIAS_VERSION; + dmdlheader.skinwidth = 256; + dmdlheader.skinheight = 256; + if (header.version != DKM2_VERSION) + { + /* has same frame structure */ + dmdlheader.framesize = header.framesize; + } + else + { + dmdlheader.framesize = sizeof(daliasframe_t) - sizeof(dtrivertx_t); + dmdlheader.framesize += header.num_xyz * sizeof(dtrivertx_t); + } + + dmdlheader.num_skins = header.num_skins; + dmdlheader.num_xyz = header.num_xyz; + dmdlheader.num_st = header.num_st; + dmdlheader.num_tris = header.num_tris; + dmdlheader.num_glcmds = header.num_glcmds; + dmdlheader.num_frames = header.num_frames; + + /* just skip header */ + dmdlheader.ofs_skins = sizeof(dmdl_t); + dmdlheader.ofs_st = dmdlheader.ofs_skins + dmdlheader.num_skins * MAX_SKINNAME; + dmdlheader.ofs_tris = dmdlheader.ofs_st + dmdlheader.num_st * sizeof(dstvert_t); + dmdlheader.ofs_frames = dmdlheader.ofs_tris + dmdlheader.num_tris * sizeof(dtriangle_t); + dmdlheader.ofs_glcmds = dmdlheader.ofs_frames + dmdlheader.num_frames * dmdlheader.framesize; + dmdlheader.ofs_end = dmdlheader.ofs_glcmds + dmdlheader.num_glcmds * sizeof(int); + + extradata = Hunk_Begin(dmdlheader.ofs_end); + pheader = Hunk_Alloc(dmdlheader.ofs_end); + memcpy(pheader, &dmdlheader, sizeof(dmdl_t)); + + memcpy ((byte*)pheader + pheader->ofs_skins, (byte *)buffer + header.ofs_skins, + pheader->num_skins * MAX_SKINNAME); + Mod_LoadSTvertList (pheader, + (dstvert_t *)((byte *)buffer + header.ofs_st)); + Mod_LoadDKMCmdList (mod_name, pheader, + (int *)((byte *)buffer + header.ofs_glcmds)); + if (header.version == DKM1_VERSION) + { + Mod_LoadFrames (pheader, (byte *)buffer + header.ofs_frames, + header.translate); + } + else + { + Mod_LoadDkmFrames (pheader, (byte *)buffer + header.ofs_frames, + header.framesize, header.translate); + } + + Mod_LoadDkmTriangleList (pheader, + (dkmtriangle_t *)((byte *)buffer + header.ofs_tris)); + + // Load in our skins. + for (i=0 ; inum_skins ; i++) + { + skins[i] = find_image((char *)pheader + pheader->ofs_skins + i*MAX_SKINNAME, + it_skin); + } + + *type = mod_alias; + + mins[0] = -32; + mins[1] = -32; + mins[2] = -32; + maxs[0] = 32; + maxs[1] = 32; + maxs[2] = 32; + + return extradata; +} + /* ================= Mod_LoadAliasModel @@ -866,6 +1118,10 @@ Mod_LoadAliasModel (const char *mod_name, const void *buffer, int modfilelen, { switch (LittleLong(*(unsigned *)buffer)) { + case DKMHEADER: + return Mod_LoadDKMModel(mod_name, buffer, modfilelen, mins, maxs, skins, + find_image, type); + case RAVENFMHEADER: return Mod_LoadFlexModel(mod_name, buffer, modfilelen, mins, maxs, skins, find_image, type); @@ -970,6 +1226,7 @@ Mod_LoadFile(char *name, void **buffer) } if (!strcmp(ext, "fm") || + !strcmp(ext, "dkm") || !strcmp(ext, "md2") || !strcmp(ext, "mdl")) { @@ -1004,6 +1261,14 @@ Mod_LoadFile(char *name, void **buffer) return filesize; } + /* Check Daikatana model */ + Q_strlcpy(newname + tlen, ".dkm", sizeof(newname)); + filesize = ri.FS_LoadFile (newname, buffer); + if (filesize > 0) + { + return filesize; + } + /* Check Quake model */ Q_strlcpy(newname + tlen, ".mdl", sizeof(newname)); filesize = ri.FS_LoadFile (newname, buffer); diff --git a/src/vk/vk_model.c b/src/vk/vk_model.c index 3b70e19..7299bdb 100644 --- a/src/vk/vk_model.c +++ b/src/vk/vk_model.c @@ -751,6 +751,8 @@ Mod_ForName (const char *name, model_t *parent_model, qboolean crash) switch (LittleLong(*(unsigned *)buf)) { + case DKMHEADER: + /* fall through */ case RAVENFMHEADER: /* fall through */ case IDALIASHEADER: