From 0a774a4f756eab54eb33aea64232f912126a73bc Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Mon, 29 Jul 2024 13:29:19 +0300 Subject: [PATCH] models: add support of MDR * https://github.com/ioquake/ioq3/blob/main/md4-readme.txt * https://github.com/UberGames/MD3View --- Makefile | 5 + README.md | 1 + src/client/menu/menu.c | 1 + src/client/refresh/files/models.c | 7 +- src/client/refresh/files/models.h | 2 + src/client/refresh/files/models_mdr.c | 420 ++++++++++++++++++++++++++ src/client/refresh/gl1/gl1_model.c | 2 + src/client/refresh/gl3/gl3_model.c | 2 + src/client/refresh/gl4/gl4_model.c | 2 + src/client/refresh/soft/sw_model.c | 2 + src/client/refresh/vk/vk_model.c | 2 + src/common/header/files.h | 112 +++++++ src/common/models.c | 12 + 13 files changed, 569 insertions(+), 1 deletion(-) create mode 100644 src/client/refresh/files/models_mdr.c diff --git a/Makefile b/Makefile index 61832e94..3d1e4aa6 100644 --- a/Makefile +++ b/Makefile @@ -1101,6 +1101,7 @@ REFGL1_OBJS_ := \ src/client/refresh/files/maps.o \ src/client/refresh/files/models.o \ src/client/refresh/files/models_md5.o \ + src/client/refresh/files/models_mdr.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/warp.o \ @@ -1140,6 +1141,7 @@ REFGL3_OBJS_ := \ src/client/refresh/files/maps.o \ src/client/refresh/files/models.o \ src/client/refresh/files/models_md5.o \ + src/client/refresh/files/models_mdr.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/warp.o \ @@ -1185,6 +1187,7 @@ REFGL4_OBJS_ := \ src/client/refresh/files/maps.o \ src/client/refresh/files/models.o \ src/client/refresh/files/models_md5.o \ + src/client/refresh/files/models_mdr.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/warp.o \ @@ -1231,6 +1234,7 @@ REFSOFT_OBJS_ := \ src/client/refresh/files/maps.o \ src/client/refresh/files/models.o \ src/client/refresh/files/models_md5.o \ + src/client/refresh/files/models_mdr.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/warp.o \ @@ -1277,6 +1281,7 @@ REFVK_OBJS_ := \ src/client/refresh/files/maps.o \ src/client/refresh/files/models.o \ src/client/refresh/files/models_md5.o \ + src/client/refresh/files/models_mdr.o \ src/client/refresh/files/stb.o \ src/client/refresh/files/wal.o \ src/client/refresh/files/warp.o \ diff --git a/README.md b/README.md index b5a55f75..271a96fe 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Models support: | dkm | Daikatana DKM1 | 8 bit | Multiple | Unchecked with game | | dkm | Daikatana DKM2 | 10 bit | Multiple | Unchecked with game | | md3 | Quake 3 | 16 bit | Multiple | No tags support | +| mdr | EliteForce | float | Multiple | No tags support. Uses first LOD only | | md5 | Doom 3/Quake 4 | float | Multiple | Requires md2 for skins | | sbm | SiN | Part of sam | Multiple | Unchecked with game | | sam | SiN | 8 bit | Multiple | Unchecked with game | diff --git a/src/client/menu/menu.c b/src/client/menu/menu.c index f070dfc6..de545198 100644 --- a/src/client/menu/menu.c +++ b/src/client/menu/menu.c @@ -5800,6 +5800,7 @@ PlayerModelList(void) ContainsFile(s, "tris.def") == false && ContainsFile(s, "tris.md2") == false && ContainsFile(s, "tris.md3") == false && + ContainsFile(s, "tris.mdr") == false && ContainsFile(s, "tris.md5mesh") == false && ContainsFile(s, "tris.mdx") == false && ContainsFile(s, "tris.mdl") == false) diff --git a/src/client/refresh/files/models.c b/src/client/refresh/files/models.c index bf24fdf4..961b0604 100644 --- a/src/client/refresh/files/models.c +++ b/src/client/refresh/files/models.c @@ -1586,7 +1586,7 @@ Mod_LoadModel_MD3(const char *mod_name, const void *buffer, int modfilelen, mesh_nodes[i].ofs_tris = num_tris; mesh_nodes[i].num_tris = LittleLong(md3_mesh->num_tris); - for (j = 0; j < LittleLong(md3_mesh->num_tris); j++) + for (j = 0; j < mesh_nodes[i].num_tris; j++) { int k; @@ -3088,6 +3088,11 @@ Mod_LoadModel(const char *mod_name, const void *buffer, int modfilelen, skins, numskins, type); break; + case MDR_IDENT: + extradata = Mod_LoadModel_MDR(mod_name, buffer, modfilelen, + skins, numskins, type); + break; + case IDMD5HEADER: extradata = Mod_LoadModel_MD5(mod_name, buffer, modfilelen, skins, numskins, type); diff --git a/src/client/refresh/files/models.h b/src/client/refresh/files/models.h index e470733a..6d4b7505 100644 --- a/src/client/refresh/files/models.h +++ b/src/client/refresh/files/models.h @@ -40,6 +40,8 @@ extern void PrepareFrameVertex(dmdx_vert_t *vertexArray, int num_verts, daliasxframe_t *frame_out); extern void *Mod_LoadModel_MD5(const char *mod_name, const void *buffer, int modfilelen, struct image_s ***skins, int *numskins, modtype_t *type); +extern void *Mod_LoadModel_MDR(const char *mod_name, const void *buffer, + int modfilelen, struct image_s ***skins, int *numskins, modtype_t *type); extern int Mod_LoadCmdCompress(const dstvert_t *texcoords, dtriangle_t *triangles, int num_tris, int *commands, int skinwidth, int skinheight); extern void Mod_LoadCmdGenerate(dmdx_t *pheader); diff --git a/src/client/refresh/files/models_mdr.c b/src/client/refresh/files/models_mdr.c new file mode 100644 index 00000000..00e869de --- /dev/null +++ b/src/client/refresh/files/models_mdr.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 1997-2005 Id Software, Inc. + * Copyright (C) 2010 Matthew Baranowski, Sander van Rossen & Raven software. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * The MDR models file format + * + * ======================================================================= + */ + +#include "models.h" + +#define MC_BITS_X (16) +#define MC_BITS_Y (16) +#define MC_BITS_Z (16) +#define MC_BITS_VECT (16) + +#define MC_SCALE_X (1.0f/64) +#define MC_SCALE_Y (1.0f/64) +#define MC_SCALE_Z (1.0f/64) + +#define MC_MASK_X ((1<<(MC_BITS_X))-1) +#define MC_MASK_Y ((1<<(MC_BITS_Y))-1) +#define MC_MASK_Z ((1<<(MC_BITS_Z))-1) +#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1) + +#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2)) + +#define MC_POS_X (0) +#define MC_SHIFT_X (0) + +#define MC_POS_Y ((((MC_BITS_X))/8)) +#define MC_SHIFT_Y ((((MC_BITS_X)%8))) + +#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8)) +#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8))) + +#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8)) +#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8))) + +#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8)) +#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8))) + +#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8)) +#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8))) + +#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8)) +#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8))) + +#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8)) +#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8))) + +#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8)) +#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8))) + +#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8)) +#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8))) + +#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8)) +#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8))) + +#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8)) +#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8))) + +static void +MC_UnCompress(float mat[3][4],const unsigned char * comp) +{ + int val; + + val=(int)((unsigned short *)(comp))[0]; + val-=1<<(MC_BITS_X-1); + mat[0][3]=((float)(val))*MC_SCALE_X; + + val=(int)((unsigned short *)(comp))[1]; + val-=1<<(MC_BITS_Y-1); + mat[1][3]=((float)(val))*MC_SCALE_Y; + + val=(int)((unsigned short *)(comp))[2]; + val-=1<<(MC_BITS_Z-1); + mat[2][3]=((float)(val))*MC_SCALE_Z; + + val=(int)((unsigned short *)(comp))[3]; + val-=1<<(MC_BITS_VECT-1); + mat[0][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[4]; + val-=1<<(MC_BITS_VECT-1); + mat[0][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[5]; + val-=1<<(MC_BITS_VECT-1); + mat[0][2]=((float)(val))*MC_SCALE_VECT; + + + val=(int)((unsigned short *)(comp))[6]; + val-=1<<(MC_BITS_VECT-1); + mat[1][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[7]; + val-=1<<(MC_BITS_VECT-1); + mat[1][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[8]; + val-=1<<(MC_BITS_VECT-1); + mat[1][2]=((float)(val))*MC_SCALE_VECT; + + + val=(int)((unsigned short *)(comp))[9]; + val-=1<<(MC_BITS_VECT-1); + mat[2][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[10]; + val-=1<<(MC_BITS_VECT-1); + mat[2][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[11]; + val-=1<<(MC_BITS_VECT-1); + mat[2][2]=((float)(val))*MC_SCALE_VECT; +} + +/* +================= +Mod_LoadModel_MDR + +================= +*/ +void * +Mod_LoadModel_MDR(const char *mod_name, const void *buffer, int modfilelen, + struct image_s ***skins, int *numskins, modtype_t *type) +{ + int framesize, ofs_skins, ofs_frames, ofs_glcmds, ofs_meshes, ofs_tris, + ofs_st, ofs_end; + int num_xyz = 0, num_tris = 0, num_glcmds = 0, num_skins = 0, meshofs = 0; + mdr_header_t pinmodel; + dmdxmesh_t *mesh_nodes; + dtriangle_t *tris; + dstvert_t *st; + void *extradata = NULL; + dmdx_t *pheader = NULL; + dmdx_vert_t *vertx; + char *skin; + int i; + + if (modfilelen < sizeof(pinmodel)) + { + R_Printf(PRINT_ALL, "%s: %s has incorrect header size (%i should be " YQ2_COM_PRIdS ")\n", + __func__, mod_name, modfilelen, sizeof(pinmodel)); + return NULL; + } + + for (i = 0; i < sizeof(pinmodel) / sizeof(int); i++) + { + ((int *)&pinmodel)[i] = LittleLong(((int *)buffer)[i]); + } + + if (pinmodel.version != MDR_VERSION) + { + R_Printf(PRINT_ALL, "%s: %s has wrong version number (%i should be %i)\n", + __func__, mod_name, pinmodel.version, MDR_VERSION); + return NULL; + } + + int unframesize = sizeof(mdr_frame_t) + sizeof(mdr_bone_t) * (pinmodel.num_bones - 1); + char *frames = malloc(unframesize * pinmodel.num_frames); + + if (pinmodel.ofs_frames < 0) + { + int compframesize = sizeof(mdr_compframe_t) + sizeof(mdr_compbone_t) * (pinmodel.num_bones - 1); + for (i = 0; i < pinmodel.num_frames; i++) + { + mdr_compframe_t * inframe = (mdr_compframe_t*)( + (byte*)buffer + -pinmodel.ofs_frames + + (i * compframesize)); + mdr_frame_t *outframe = (mdr_frame_t *)(frames + i * unframesize); + int j; + + memset(outframe->name, 0, sizeof(outframe->name)); + for (j = 0; j < 3; j++) + { + outframe->bounds[0][j] = LittleFloat(inframe->bounds[0][j]); + outframe->bounds[1][j] = LittleFloat(inframe->bounds[1][j]); + outframe->origin[j] = LittleFloat(inframe->origin[j]); + outframe->radius = LittleFloat(inframe->radius); + } + for (j = 0; j < pinmodel.num_bones; j ++) + { + MC_UnCompress(outframe->bones[j].matrix, inframe->bones[j].Comp); + } + } + } + else + { + for (i = 0; i < pinmodel.num_frames; i++) + { + mdr_frame_t * inframe = (mdr_frame_t*)( + (byte*)buffer + pinmodel.ofs_frames + + (i * unframesize)); + mdr_frame_t *outframe = (mdr_frame_t *)(frames + i * unframesize); + int j; + + memcpy(outframe->name, inframe->name, sizeof(outframe->name)); + for (j = 0; j < 3; j++) + { + outframe->bounds[0][j] = LittleFloat(inframe->bounds[0][j]); + outframe->bounds[1][j] = LittleFloat(inframe->bounds[1][j]); + outframe->origin[j] = LittleFloat(inframe->origin[j]); + outframe->radius = LittleFloat(inframe->radius); + } + + int float_count = pinmodel.num_bones * sizeof(mdr_bone_t); + float *infloat, *outfloat; + infloat = (float *)&inframe->bones; + outfloat = (float *)&outframe->bones; + for (j = 0; j < float_count; j ++) + { + outfloat[j] = LittleFloat(infloat[j]); + } + } + } + + mdr_lod_t *inlod; + inlod = (mdr_lod_t*)(buffer + pinmodel.ofs_lods); + + meshofs = inlod->ofs_surfaces; + for (i = 0; i < inlod->num_surfaces; i++) + { + mdr_surface_t* insurf; + + insurf = (mdr_surface_t*)((char*)inlod + meshofs); + num_tris += LittleLong(insurf->num_tris); + num_xyz += LittleLong(insurf->num_verts); + meshofs += LittleLong(insurf->ofs_end); + } + + num_skins = inlod->num_surfaces; + + /* (count vert + 3 vert * (2 float + 1 int)) + final zero; */ + num_glcmds = (10 * num_tris) + 1 * inlod->num_surfaces; + + framesize = sizeof(daliasxframe_t) + sizeof(dxtrivertx_t) * num_xyz; + ofs_skins = sizeof(dmdx_t); + ofs_frames = ofs_skins + num_skins * MAX_SKINNAME; + ofs_glcmds = ofs_frames + framesize * pinmodel.num_frames; + ofs_meshes = ofs_glcmds + num_glcmds * sizeof(int); + ofs_tris = ofs_meshes + inlod->num_surfaces * sizeof(dmdxmesh_t); + ofs_st = ofs_tris + num_tris * sizeof(dtriangle_t); + ofs_end = ofs_st + num_xyz * sizeof(dstvert_t); + + *numskins = num_skins; + extradata = Hunk_Begin(ofs_end + Q_max(*numskins, MAX_MD2SKINS) * sizeof(struct image_s *)); + pheader = Hunk_Alloc(ofs_end); + *skins = Hunk_Alloc((*numskins) * sizeof(struct image_s *)); + + pheader->framesize = framesize; + pheader->skinheight = 256; + pheader->skinwidth = 256; + pheader->num_skins = *numskins; + pheader->num_glcmds = num_glcmds; + pheader->num_frames = pinmodel.num_frames; + pheader->num_xyz = num_xyz; + pheader->num_meshes = inlod->num_surfaces; + pheader->num_st = num_xyz; + pheader->num_tris = num_tris; + pheader->ofs_meshes = ofs_meshes; + pheader->ofs_skins = ofs_skins; + pheader->ofs_st = ofs_st; + pheader->ofs_tris = ofs_tris; + pheader->ofs_frames = ofs_frames; + pheader->ofs_glcmds = ofs_glcmds; + pheader->ofs_end = ofs_end; + + mesh_nodes = (dmdxmesh_t *)((byte *)pheader + pheader->ofs_meshes); + tris = (dtriangle_t*)((byte *)pheader + pheader->ofs_tris); + st = (dstvert_t*)((byte *)pheader + pheader->ofs_st); + vertx = malloc(pinmodel.num_frames * pheader->num_xyz * sizeof(dmdx_vert_t)); + skin = (char *)pheader + pheader->ofs_skins; + + num_xyz = 0; + num_tris = 0; + meshofs = inlod->ofs_surfaces; + + for (i = 0; i < inlod->num_surfaces; i++) + { + mdr_surface_t* insurf; + const int *p; + int j; + + insurf = (mdr_surface_t*)((char*)inlod + meshofs); + + /* load shaders */ + memcpy(skin, insurf->shader, Q_min(sizeof(insurf->shader), MAX_SKINNAME)); + skin += MAX_SKINNAME; + + /* load triangles */ + p = (const int*)((byte*)insurf + LittleLong(insurf->ofs_tris)); + + mesh_nodes[i].ofs_tris = num_tris; + mesh_nodes[i].num_tris = LittleLong(insurf->num_tris); + + for (j = 0; j < mesh_nodes[i].num_tris; j++) + { + int k; + + for (k = 0; k < 3; k++) + { + int vert_id; + + /* index */ + vert_id = LittleLong(p[j * 3 + k]) + num_xyz; + tris[num_tris + j].index_xyz[k] = vert_id; + tris[num_tris + j].index_st[k] = vert_id; + } + } + + /* load vertex */ + mdr_vertex_t * inVert = (mdr_vertex_t *)((char*)insurf + insurf->ofs_verts); + for (j = 0; j < insurf->num_verts; j ++) + { + int f; + + st[j + num_xyz].s = LittleFloat(inVert->texcoords[0]) * pheader->skinwidth; + st[j + num_xyz].t = LittleFloat(inVert->texcoords[1]) * pheader->skinheight; + + for (f = 0; f < pinmodel.num_frames; f ++) + { + int k, vert_pos; + mdr_frame_t *outframe = (mdr_frame_t *)(frames + f * unframesize); + vec3_t tempVert, tempNormal; + mdr_weight_t *w; + + vert_pos = num_xyz + f * pheader->num_xyz + j; + + VectorClear(tempVert); + VectorClear(tempNormal); + + w = inVert->weights; + for ( k = 0; k < LittleLong(inVert->num_weights); k++, w++) + { + mdr_bone_t *bone; + float bone_weight; + vec3_t innorm, inoffset; + int n; + + bone = outframe->bones + LittleLong(w->bone_index); + + VectorCopy(w->offset, inoffset); + VectorCopy(inVert->normal, innorm); + for (n = 0; n < 3; n++) + { + inoffset[n] = LittleFloat(inoffset[n]); + innorm[n] = LittleFloat(innorm[n]); + } + bone_weight = LittleFloat(w->bone_weight); + + for (n = 0; n < 3; n++) + { + tempVert[n] += bone_weight * (DotProduct(bone->matrix[n], inoffset) + bone->matrix[n][3]); + tempNormal[n] += bone_weight * DotProduct(bone->matrix[n], innorm); + } + } + + VectorCopy(tempVert, vertx[vert_pos].xyz); + VectorCopy(tempNormal, vertx[vert_pos].norm); + } + inVert = (mdr_vertex_t *)((char *)inVert + + sizeof(mdr_vertex_t) + sizeof(mdr_weight_t) * (inVert->num_weights - 1)); + } + + num_tris += LittleLong(insurf->num_tris); + num_xyz += LittleLong(insurf->num_verts); + meshofs += LittleLong(insurf->ofs_end); + } + + for (i = 0; i < pheader->num_frames; i ++) + { + daliasxframe_t *frame = (daliasxframe_t *)( + (byte *)pheader + pheader->ofs_frames + i * pheader->framesize); + const mdr_frame_t *outframe = (mdr_frame_t *)(frames + i * unframesize); + + if (outframe->name[0]) + { + strncpy(frame->name, outframe->name, sizeof(frame->name) - 1); + } + else + { + snprintf(frame->name, 15, "%d", i); + } + + PrepareFrameVertex(vertx + i * pheader->num_xyz, + pheader->num_xyz, frame); + } + + free(vertx); + free(frames); + + Mod_LoadCmdGenerate(pheader); + + Mod_LoadFixImages(mod_name, pheader, false); + + *type = mod_alias; + + return extradata; +} diff --git a/src/client/refresh/gl1/gl1_model.c b/src/client/refresh/gl1/gl1_model.c index 2c31fbca..550c1303 100644 --- a/src/client/refresh/gl1/gl1_model.c +++ b/src/client/refresh/gl1/gl1_model.c @@ -491,6 +491,8 @@ Mod_ForName(const char *name, model_t *parent_model, qboolean crash) /* fall through */ case ID3HEADER: /* fall through */ + case MDR_IDENT: + /* fall through */ case IDMD5HEADER: /* fall through */ case IDSPRITEHEADER: diff --git a/src/client/refresh/gl3/gl3_model.c b/src/client/refresh/gl3/gl3_model.c index 9d1eb9f8..1b81363e 100644 --- a/src/client/refresh/gl3/gl3_model.c +++ b/src/client/refresh/gl3/gl3_model.c @@ -492,6 +492,8 @@ Mod_ForName(const char *name, gl3model_t *parent_model, qboolean crash) /* fall through */ case ID3HEADER: /* fall through */ + case MDR_IDENT: + /* fall through */ case IDMD5HEADER: /* fall through */ case IDSPRITEHEADER: diff --git a/src/client/refresh/gl4/gl4_model.c b/src/client/refresh/gl4/gl4_model.c index 6159e0e3..1ca12637 100644 --- a/src/client/refresh/gl4/gl4_model.c +++ b/src/client/refresh/gl4/gl4_model.c @@ -492,6 +492,8 @@ Mod_ForName(const char *name, gl4model_t *parent_model, qboolean crash) /* fall through */ case ID3HEADER: /* fall through */ + case MDR_IDENT: + /* fall through */ case IDMD5HEADER: /* fall through */ case IDSPRITEHEADER: diff --git a/src/client/refresh/soft/sw_model.c b/src/client/refresh/soft/sw_model.c index ec1f3dd1..a0ef20a4 100644 --- a/src/client/refresh/soft/sw_model.c +++ b/src/client/refresh/soft/sw_model.c @@ -499,6 +499,8 @@ Mod_ForName(const char *name, model_t *parent_model, qboolean crash) /* fall through */ case ID3HEADER: /* fall through */ + case MDR_IDENT: + /* fall through */ case IDMD5HEADER: /* fall through */ case IDSPRITEHEADER: diff --git a/src/client/refresh/vk/vk_model.c b/src/client/refresh/vk/vk_model.c index 36052110..1024effa 100644 --- a/src/client/refresh/vk/vk_model.c +++ b/src/client/refresh/vk/vk_model.c @@ -475,6 +475,8 @@ Mod_ForName(const char *name, model_t *parent_model, qboolean crash) /* fall through */ case ID3HEADER: /* fall through */ + case MDR_IDENT: + /* fall through */ case IDMD5HEADER: /* fall through */ case IDSPRITEHEADER: diff --git a/src/common/header/files.h b/src/common/header/files.h index a18ff166..079809eb 100644 --- a/src/common/header/files.h +++ b/src/common/header/files.h @@ -1076,6 +1076,118 @@ typedef struct int ofs_end; // end of file } sin_sam_header_t; +/* + * Here are the definitions for Ravensoft's model format of md4. Raven stores their + * playermodels in .mdr files, in some games, which are pretty much like the md4 + * format implemented by ID soft. It seems like ID's original md4 stuff is not used at all. + * MDR is being used in EliteForce, JediKnight2 and Soldiers of Fortune2 (I think). + * So this comes in handy for anyone who wants to make it possible to load player + * models from these games. + * This format has bone tags, which is similar to the thing you have in md3 I suppose. + * Raven has released their version of md3view under GPL enabling me to add support + * to this codebase. Thanks to Steven Howes aka Skinner for helping with example + * source code. + * + * - Thilo Schulz (arny@ats.s.bawue.de) + */ + +#define MDR_IDENT (('5'<<24)+('M'<<16)+('D'<<8)+'R') +#define MDR_VERSION 2 + +typedef struct { + int bone_index; // these are indexes into the boneReferences, + float bone_weight; // not the global per-frame bone list + vec3_t offset; +} mdr_weight_t; + +typedef struct { + vec3_t normal; + vec2_t texcoords; + int num_weights; + mdr_weight_t weights[1]; // variable sized +} mdr_vertex_t; + +typedef struct { + int ident; + + char name[MAX_QPATH]; // polyset name + char shader[MAX_QPATH]; + int shader_index; // for in-game use + + int ofs_header; // this will be a negative number + + int num_verts; + int ofs_verts; + + int num_tris; + int ofs_tris; + + // Bone references are a set of ints representing all the bones + // present in any vertex weights for this surface. This is + // needed because a model may have surfaces that need to be + // drawn at different sort times, and we don't want to have + // to re-interpolate all the bones for each surface. + int num_bonereferences; + int ofs_bonereferences; + + int ofs_end; // next surface follows +} mdr_surface_t; + +typedef struct { + float matrix[3][4]; +} mdr_bone_t; + +typedef struct { + vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame + vec3_t origin; // midpoint of bounds, used for sphere cull + float radius; // dist from origin to corner + char name[16]; + mdr_bone_t bones[1]; // [num_bones] +} mdr_frame_t; + +typedef struct { + unsigned char Comp[24]; // MC_COMP_BYTES is in MatComp.h, but don't want to couple +} mdr_compbone_t; + +typedef struct { + vec3_t bounds[2]; // bounds of all surfaces of all LOD's for this frame + vec3_t origin; // midpoint of bounds, used for sphere cull + float radius; // dist from origin to corner + mdr_compbone_t bones[1]; // [num_bones] +} mdr_compframe_t; + +typedef struct { + int num_surfaces; + int ofs_surfaces; // first surface, others follow + int ofs_end; // next lod follows +} mdr_lod_t; + +typedef struct { + int bone_index; + char name[32]; +} mdr_tag_t; + +typedef struct { + int ident; + int version; + + char name[MAX_QPATH]; // model name + + // frames and bones are shared by all levels of detail + int num_frames; + int num_bones; + int ofs_frames; // mdr_frame_t[numFrames] + + // each level of detail has completely separate sets of surfaces + int num_lods; + int ofs_lods; + + int num_tags; + int ofs_tags; + + int ofs_end; // end of file +} mdr_header_t; + /* Quake 1 BSP */ #define BSPQ1VERSION 29 diff --git a/src/common/models.c b/src/common/models.c index 9c717c8b..65e39dfb 100644 --- a/src/common/models.c +++ b/src/common/models.c @@ -221,6 +221,7 @@ Mod_LoadFileWithoutExt(const char *namewe, void **buffer, const char* ext) !strcmp(ext, "dkm") || !strcmp(ext, "md2") || !strcmp(ext, "md3") || + !strcmp(ext, "mdr") || !strcmp(ext, "md5mesh") || !strcmp(ext, "mdx") || !strcmp(ext, "mdl")) @@ -236,6 +237,17 @@ Mod_LoadFileWithoutExt(const char *namewe, void **buffer, const char* ext) return filesize; } + /* Check Quake 3 model */ + Q_strlcpy(newname, namewe, sizeof(newname)); + Q_strlcpy(newname + tlen, ".mdr", sizeof(newname)); + filesize = FS_LoadFile(newname, buffer); + if (filesize > 0) + { + Com_DPrintf("%s: %s loaded as mdr/md4 (Star Trek: Elite Force)\n", + __func__, namewe); + return filesize; + } + /* Check Quake 3 model */ Q_strlcpy(newname, namewe, sizeof(newname)); Q_strlcpy(newname + tlen, ".md3", sizeof(newname));