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/)
This commit is contained in:
Denis Pauk 2023-02-17 23:48:08 +02:00
parent 40af78f13a
commit c6ce695406
4 changed files with 337 additions and 32 deletions

View file

@ -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.

View file

@ -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 */

View file

@ -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 ; i<pheader->num_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 ; i<pheader->num_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 ; i<num_tris ; i++)
{
@ -448,6 +582,7 @@ Mod_LoadMD2 (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)
{
vec3_t translate = {0, 0, 0};
dmdl_t *pinmodel, *pheader;
dtriangle_t *pintri;
dstvert_t *pinst;
@ -539,7 +674,7 @@ Mod_LoadMD2 (const char *mod_name, const void *buffer, int modfilelen,
//
// load the frames
//
Mod_LoadFrames (pheader, (byte *)pinmodel + pheader->ofs_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<sizeof(dkm_header_t)/sizeof(int) ; i++)
((int *)&header)[i] = LittleLong (((int *)buffer)[i]);
if (header.ident != DKMHEADER)
{
R_Printf(PRINT_ALL, "%s: %s has wrong ident (%i should be %i)",
__func__, mod_name, header.ident, DKMHEADER);
return NULL;
}
if (header.version != DKM1_VERSION && header.version != DKM2_VERSION)
{
R_Printf(PRINT_ALL, "%s: %s has wrong version number (%i should be %i)",
__func__, mod_name, header.version, DKM2_VERSION);
return NULL;
}
if (header.ofs_end < 0 || header.ofs_end > 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 ; i<pheader->num_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);

View file

@ -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: