models: Add Anachronox format support

Based on https://github.com/hogsy/chronon code.

Does not support normals and could have issues with scale.
This commit is contained in:
Denis Pauk 2024-02-08 00:52:11 +02:00
parent b3a84afcfa
commit af6d0b26bd
2 changed files with 371 additions and 23 deletions

View file

@ -75,6 +75,51 @@ Mod_LoadCmdList(const char *mod_name, dmdx_t *pheader, const int *pincmd)
}
}
/*
* verts as bytes
*/
static void
Mod_LoadFrames_VertMD2(dxtrivertx_t *vert, const byte *in)
{
int k;
for (k=0; k < 3; k++)
{
vert->v[k] = in[k] * 0xFF;
}
}
/*
* verts as compressed int
*/
static void
Mod_LoadFrames_VertDKM2(dxtrivertx_t *vert, int in)
{
unsigned xyz;
xyz = LittleLong(in) & 0xFFFFFFFF;
vert->v[0] = ((xyz & 0xFFE00000) >> 21) & 0x7FF;
vert->v[0] *= ((float)0xFFFF / 0x7FF);
vert->v[1] = ((xyz & 0x1FF800) >> 11) & 0x3FF;
vert->v[1] *= ((float)0xFFFF / 0x3FF);
vert->v[2] = xyz & 0x7FF;
vert->v[2] *= ((float)0xFFFF / 0x7FF);
}
/*
* verts as short
*/
static void
Mod_LoadFrames_VertAnox(dxtrivertx_t *vert, const short *in)
{
int k;
for (k=0; k < 3; k++)
{
vert->v[k] = LittleShort(in[k]);
}
}
/*
=================
Mod_LoadFrames
@ -98,7 +143,7 @@ Mod_LoadFrames_MD2(dmdx_t *pheader, byte *src, size_t inframesize, vec3_t transl
+ pheader->ofs_frames + i * pheader->framesize);
memcpy(poutframe->name, pinframe->name, sizeof(poutframe->name));
for (j=0 ; j<3 ; j++)
for (j = 0; j < 3; j++)
{
poutframe->scale[j] = LittleFloat(pinframe->scale[j]) / 0xFF;
poutframe->translate[j] = LittleFloat(pinframe->translate[j]);
@ -107,18 +152,101 @@ Mod_LoadFrames_MD2(dmdx_t *pheader, byte *src, size_t inframesize, vec3_t transl
// verts are all 8 bit, so no swapping needed
for (j=0; j < pheader->num_xyz; j ++)
{
int k;
for (k=0; k < 3; k++)
{
poutframe->verts[j].v[k] = pinframe->verts[j].v[k] * 0xFF;
}
Mod_LoadFrames_VertMD2(poutframe->verts + j, pinframe->verts[j].v);
poutframe->verts[j].lightnormalindex = pinframe->verts[j].lightnormalindex;
}
}
}
/*
=================
Mod_LoadFrames_MD2Anox
Load the Anachronox md2 format frame
=================
*/
static void
Mod_LoadFrames_MD2Anox(dmdx_t *pheader, byte *src, size_t inframesize,
vec3_t translate, int resolution)
{
int i;
for (i = 0; i < pheader->num_frames; i++)
{
daliasframe_t *pinframe;
daliasxframe_t *poutframe;
byte *inverts;
int j;
pinframe = (daliasframe_t *) (src + i * inframesize);
poutframe = (daliasxframe_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];
}
inverts = (byte*)pinframe->verts;
switch(resolution)
{
case 0:
/* Code will multiply vertex values by 255 */
for (j = 0; j < 3; j++)
{
poutframe->scale[j] /= 0xFF;
}
/* verts are all 8 bit, so no swapping needed */
for (j=0; j < pheader->num_xyz; j ++)
{
Mod_LoadFrames_VertMD2(poutframe->verts + j, inverts);
/* 3 bytes vert + 2 bytes */
inverts += 5;
/* norm convert logic is unknown */
poutframe->verts[j].lightnormalindex = 0;
}
break;
case 1:
/* Code will multiply vertex for normilize */
poutframe->scale[0] *= (0x7FF / (float)0xFFFF);
poutframe->scale[1] *= (0x3FF / (float)0xFFFF);
poutframe->scale[2] *= (0x7FF / (float)0xFFFF);
/* verts are 32 bit and swap are inside vonvert code*/
for (j=0; j < pheader->num_xyz; j ++)
{
Mod_LoadFrames_VertDKM2(poutframe->verts + j, *((int *)inverts));
/* int vert + 2 bytes */
inverts += 6;
/* norm convert logic is unknown */
poutframe->verts[j].lightnormalindex = 0;
}
break;
case 2:
/* verts are all short, swapped inside func */
for (j=0; j < pheader->num_xyz; j ++)
{
Mod_LoadFrames_VertAnox(poutframe->verts + j, (short*)inverts);
/* 6 bytes vert + 2 bytes */
inverts += 8;
/* norm convert logic is unknown */
poutframe->verts[j].lightnormalindex = 0;
}
break;
default:
/* should never happen */
break;
}
}
}
/*
=================
Mod_LoadDTriangleList
@ -134,11 +262,11 @@ Mod_LoadDTriangleList(dmdx_t *pheader, const dtriangle_t *pintri)
pouttri = (dtriangle_t *) ((byte *)pheader + pheader->ofs_tris);
for (i=0 ; i<pheader->num_tris ; i++)
for (i = 0; i < pheader->num_tris; i++)
{
int j;
for (j=0 ; j<3 ; 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]);
@ -272,15 +400,7 @@ Mod_LoadFrames_DKM2(dmdx_t *pheader, const byte *src, size_t inframesize, vec3_t
/* 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) & 0x7FF;
outverts[j].v[0] *= ((float)0xFFFF / 0x7FF);
outverts[j].v[1] = ((xyz & 0x1FF800) >> 11) & 0x3FF;
outverts[j].v[1] *= ((float)0xFFFF / 0x3FF);
outverts[j].v[2] = xyz & 0x7FF;
outverts[j].v[2] *= ((float)0xFFFF / 0x7FF);
Mod_LoadFrames_VertDKM2(outverts + j, *((int *)inverts));
inverts += sizeof(int);
outverts[j].lightnormalindex = *inverts;
inverts ++;
@ -495,7 +615,7 @@ Mod_LoadModel_MDL(const char *mod_name, const void *buffer, int modfilelen,
{
int j;
for (j=0 ; j<3 ; j++)
for (j = 0; j < 3; j++)
{
pouttri[i].index_xyz[j] = LittleLong(triangles[i].vertex[j]);
pouttri[i].index_st[j] = pouttri[i].index_xyz[j];
@ -848,6 +968,179 @@ Mod_LoadModel_MD3(const char *mod_name, const void *buffer, int modfilelen,
return extradata;
}
/*
=================
Mod_LoadModel_MD2Anox
ANACHRONOX Model
=================
*/
static void *
Mod_LoadModel_MD2Anox(const char *mod_name, const void *buffer, int modfilelen,
struct image_s ***skins, int *numskins, modtype_t *type)
{
vec3_t translate = {0, 0, 0};
dmdla_t pinmodel;
dmdx_t *pheader;
dtriangle_t *pintri;
dstvert_t *pinst;
dmdxmesh_t *mesh_nodes;
int *pincmd;
void *extradata;
int i;
int framesize, ofs_meshes, ofs_skins, ofs_st, ofs_tris, ofs_glcmds,
ofs_frames, ofs_end;
if (modfilelen < sizeof(pinmodel))
{
R_Printf(PRINT_ALL, "%s: %s has incorrect header size (%i should be %ld)",
__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 != ALIAS_ANACHRONOX_VERSION)
{
R_Printf(PRINT_ALL, "%s: %s has wrong version number (%i should be %i)",
__func__, mod_name, pinmodel.version, ALIAS_ANACHRONOX_VERSION);
return NULL;
}
if (pinmodel.ofs_end < 0 || pinmodel.ofs_end > modfilelen)
{
R_Printf(PRINT_ALL, "%s: model %s file size(%d) too small, should be %d",
__func__, mod_name, modfilelen, pinmodel.ofs_end);
return NULL;
}
if (pinmodel.num_skins < 0)
{
R_Printf(PRINT_ALL, "%s: model %s file has incorrect skins count %d",
__func__, mod_name, pinmodel.num_skins);
return NULL;
}
if (pinmodel.resolution < 0 || pinmodel.resolution > 2)
{
R_Printf(PRINT_ALL, "%s: model %s file has incorrect vert type %d",
__func__, mod_name, pinmodel.resolution);
return NULL;
}
if (pinmodel.num_xyz <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no vertices",
__func__, mod_name);
return NULL;
}
if (pinmodel.num_xyz > MAX_VERTS)
{
R_Printf(PRINT_ALL, "%s: model %s has too many vertices",
__func__, mod_name);
}
if (pinmodel.num_st <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no st vertices",
__func__, mod_name);
return NULL;
}
if (pinmodel.num_tris <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no triangles",
__func__, mod_name);
return NULL;
}
if (pinmodel.num_frames <= 0)
{
R_Printf(PRINT_ALL, "%s: model %s has no frames",
__func__, mod_name);
return NULL;
}
framesize = sizeof(daliasxframe_t) +
(pinmodel.num_xyz - 1) * sizeof(dxtrivertx_t);
ofs_meshes = sizeof(*pheader); // just skip header and go
ofs_skins = ofs_meshes + 1 * sizeof(dmdxmesh_t);
ofs_st = ofs_skins + pinmodel.num_skins * MAX_SKINNAME;
ofs_tris = ofs_st + pinmodel.num_st * sizeof(dstvert_t);
ofs_glcmds = ofs_tris + pinmodel.num_tris * sizeof(dtriangle_t);
ofs_frames = ofs_glcmds + pinmodel.num_glcmds * sizeof(int);
ofs_end = ofs_frames + framesize * pinmodel.num_frames;
*numskins = pinmodel.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 *));
/* Copy values as we have mostly same data format */
memset(pheader, 0, sizeof(*pheader));
pheader->skinwidth = pinmodel.skinwidth;
pheader->skinheight = pinmodel.skinheight;
pheader->framesize = framesize;
pheader->num_meshes = 1;
pheader->num_skins = pinmodel.num_skins;
pheader->num_xyz = pinmodel.num_xyz;
pheader->num_st = pinmodel.num_st;
pheader->num_tris = pinmodel.num_tris;
pheader->num_glcmds = pinmodel.num_glcmds;
pheader->num_frames = pinmodel.num_frames;
pheader->ofs_meshes = ofs_meshes;
pheader->ofs_skins = ofs_skins;
pheader->ofs_st = ofs_st;
pheader->ofs_tris = ofs_tris;
pheader->ofs_glcmds = ofs_glcmds;
pheader->ofs_frames = ofs_frames;
pheader->ofs_end = ofs_end;
/* create single mesh */
mesh_nodes = (dmdxmesh_t *)((char *)pheader + pheader->ofs_meshes);
mesh_nodes[0].start = 0;
mesh_nodes[0].num = pheader->num_glcmds;
//
// load base s and t vertices (not used in gl version)
//
pinst = (dstvert_t *)((byte *)buffer + pinmodel.ofs_st);
Mod_LoadSTvertList(pheader, pinst);
//
// load triangle lists
//
pintri = (dtriangle_t *)((byte *)buffer + pinmodel.ofs_tris);
Mod_LoadDTriangleList(pheader, pintri);
//
// load the frames
//
Mod_LoadFrames_MD2Anox(pheader, (byte *)buffer + pinmodel.ofs_frames,
pinmodel.framesize, translate, pinmodel.resolution);
//
// load the glcmds
//
pincmd = (int *)((byte *)buffer + pinmodel.ofs_glcmds);
Mod_LoadCmdList(mod_name, pheader, pincmd);
// register all skins
memcpy((char *)pheader + pheader->ofs_skins, (char *)buffer + pinmodel.ofs_skins,
pheader->num_skins*MAX_SKINNAME);
*type = mod_alias;
return extradata;
}
/*
=================
Mod_LoadModel_MD2
@ -1649,7 +1942,8 @@ Mod_LoadModel(const char *mod_name, const void *buffer, int modfilelen,
{
void *extradata = NULL;
if (!buffer || modfilelen < sizeof(unsigned))
/* code needs at least 2 ints for detect file type */
if (!buffer || modfilelen < (sizeof(unsigned) * 2))
{
return NULL;
}
@ -1667,8 +1961,22 @@ Mod_LoadModel(const char *mod_name, const void *buffer, int modfilelen,
break;
case IDALIASHEADER:
extradata = Mod_LoadModel_MD2(mod_name, buffer, modfilelen,
skins, numskins, type);
{
/* next short after file type */
short version;
version = LittleShort(((short*)buffer)[2]);
if (version == ALIAS_ANACHRONOX_VERSION)
{
extradata = Mod_LoadModel_MD2Anox(mod_name, buffer, modfilelen,
skins, numskins, type);
}
else
{
extradata = Mod_LoadModel_MD2(mod_name, buffer, modfilelen,
skins, numskins, type);
}
}
break;
case IDMDLHEADER:

View file

@ -184,6 +184,46 @@ typedef struct
int ofs_end; /* end of file */
} dmdl_t;
/* .MD2 Anachronox triangle model file format */
#define ALIAS_ANACHRONOX_VERSION 15
typedef struct
{
int ident;
short version;
short resolution;
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 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_end; /* end of file */
/* Multiple surfaces */
int num_surfaces;
int ofs_surfaces;
/* Level of detail */
vec3_t lod_scale;
/* Tagged surfaces */
int num_tagged_triangles;
int ofs_tagged_triangles;
} dmdla_t;
/* .FM triangle model file format */
#define RAVENFMHEADER (('d' << 24) + ('a' << 16) + ('e' << 8) + 'h')