Skip to content

Commit 77a0cc5

Browse files
committed
MD5 models : render each mesh independently
Rework from #718 : multi-mesh still work but each mesh is rendered separately, so be able to load it's own textures properly and not just the first surface textures, which was working by pure luck before. This prepares MD3 support, where a model can have multiple surfaces as well, a.k.a "meshes"
1 parent 5827e9e commit 77a0cc5

8 files changed

Lines changed: 266 additions & 246 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Packaging/AppImage/vkquake*
2626
Packaging/Windows/vkQuake-Installer-*.exe
2727
Packaging/Windows/vkQuake*.zip
2828
Misc/vq_pak/mkpak
29-
Misc/vq_pak/bintoc
29+
Misc/vq_pak/*.exe
3030
Misc/vq_pak/*.dSYM
3131
build/
3232
.settings

Quake/common.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
3434
#define MZ_REALLOC(p, x) Mem_Realloc (p, x)
3535

3636
// include miniz stb-syle, directly in this compilation unit.
37-
#define MINIZ_HEADER_FILE_ONLY
37+
// (supported by miniz)
3838
#include "miniz.c"
3939

4040
static char *largv[MAX_NUM_ARGVS + 1];

Quake/gl_mesh.c

Lines changed: 122 additions & 117 deletions
Large diffs are not rendered by default.

Quake/gl_model.c

Lines changed: 87 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3031,6 +3031,8 @@ Mod_LoadAliasGroup
30313031
*/
30323032
void *Mod_LoadAliasGroup (void *pin, aliashdr_t *pheader, const int index)
30333033
{
3034+
assert (pheader->poseverttype == PV_QUAKE1);
3035+
30343036
maliasframedesc_t *frame = &pheader->frames[index];
30353037
daliasgroup_t *pingroup;
30363038
int i, numframes;
@@ -3378,6 +3380,8 @@ Mod_LoadAllSkins
33783380
*/
33793381
void *Mod_LoadAllSkins (aliashdr_t *pheader, qmodel_t *mod, byte *mod_base, int numskins, byte *pskintype)
33803382
{
3383+
assert (pheader->poseverttype == PV_QUAKE1);
3384+
33813385
if (numskins < 1 || numskins > MAX_SKINS)
33823386
Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d", numskins);
33833387

@@ -4482,17 +4486,13 @@ Mod_LoadMD5MeshModel
44824486
*/
44834487
static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer)
44844488
{
4485-
const char *fname = mod->name;
4486-
unsigned short *poutindexes = NULL;
4487-
md5vert_t *poutvertexes = NULL;
4488-
aliashdr_t *outhdr, *surf;
4489-
size_t hdrsize;
4490-
size_t numjoints, j;
4491-
size_t nummeshes, m;
4492-
char texname[MAX_QPATH];
4493-
md5vertinfo_t *vinfo = NULL;
4494-
md5weightinfo_t *weight = NULL;
4495-
size_t numweights;
4489+
const char *fname = mod->name;
4490+
4491+
aliashdr_t *outhdr, *surf;
4492+
size_t hdrsize;
4493+
size_t numjoints, j;
4494+
size_t nummeshes, m;
4495+
char texname[MAX_QPATH];
44964496

44974497
md5animctx_t anim = {NULL};
44984498

@@ -4519,10 +4519,14 @@ static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer)
45194519

45204520
hdrsize = sizeof (*outhdr) - sizeof (outhdr->frames);
45214521
hdrsize += sizeof (outhdr->frames) * anim.numposes;
4522-
outhdr = (aliashdr_t *)Mem_Alloc (hdrsize * numjoints);
4522+
4523+
// alloc all aliashdr_t and their chained nextsurface, a.k.a nummeshes, in one array
4524+
outhdr = (aliashdr_t *)Mem_Alloc (hdrsize * nummeshes);
4525+
45234526
TEMP_ALLOC_ZEROED (jointinfo_t, joint_infos, numjoints);
45244527
TEMP_ALLOC_ZEROED (jointpose_t, joint_poses, numjoints);
45254528

4529+
// 1. Load joints
45264530
MD5EXPECT ("{");
45274531
for (j = 0; j < numjoints; j++)
45284532
{
@@ -4558,18 +4562,40 @@ static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer)
45584562
MD5Anim_Load (&anim, joint_infos, numjoints);
45594563
buffer = COM_Parse (buffer);
45604564

4561-
int index_offset = 0;
4562-
int vertex_offset = 0;
4563-
int weight_offset = 0;
4565+
// 2. Compute inverted joints:
4566+
TEMP_ALLOC_ZEROED (jointpose_t, inverted_joints, anim.numjoints * anim.numposes);
4567+
TEMP_ALLOC_ZEROED (jointpose_t, concat_joints, anim.numjoints);
4568+
for (size_t pose_index = 0; pose_index < anim.numposes; ++pose_index)
4569+
{
4570+
const jointpose_t *in_pose = anim.posedata + (pose_index * anim.numjoints);
4571+
const jointpose_t *out_pose = inverted_joints + (pose_index * anim.numjoints);
4572+
for (size_t joint_index = 0; joint_index < anim.numjoints; ++joint_index)
4573+
{
4574+
// concat it onto the parent (relative->abs)
4575+
if (joint_infos[joint_index].parent < 0)
4576+
memcpy (concat_joints[joint_index].mat, in_pose[joint_index].mat, sizeof (jointpose_t));
4577+
else
4578+
R_ConcatTransforms (
4579+
(void *)concat_joints[joint_infos[joint_index].parent].mat, (void *)in_pose[joint_index].mat, (void *)concat_joints[joint_index].mat);
4580+
// and finally invert it
4581+
R_ConcatTransforms ((void *)concat_joints[joint_index].mat, (void *)joint_infos[joint_index].inverse.mat, (void *)out_pose[joint_index].mat);
4582+
}
4583+
}
4584+
Mem_Free (anim.posedata);
45644585

4565-
size_t total_numverts = 0;
4566-
size_t total_numweights = 0;
4586+
// 3. each mesh has its own aliashdr_t : load vertices, triangles, textures...etc. and upload to GPU each surface:
4587+
4588+
// total_numverts and total_vertexes accumulate all vertices of the ssurface,
4589+
// just to be able to Mod_CalcAliasBounds at the end.
4590+
size_t total_numverts = 0;
4591+
md5vert_t *total_vertexes = NULL;
45674592

45684593
for (m = 0; m < nummeshes; m++)
45694594
{
45704595
MD5EXPECT ("mesh");
45714596
MD5EXPECT ("{");
45724597

4598+
// go to the surf, a.k.a mesh, chaining the next nextsurface
45734599
surf = (aliashdr_t *)((byte *)outhdr + m * hdrsize);
45744600
if (m + 1 < nummeshes)
45754601
surf->nextsurface = (aliashdr_t *)((byte *)outhdr + (m + 1) * hdrsize);
@@ -4596,6 +4622,7 @@ static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer)
45964622
surf->numframes = j;
45974623
}
45984624

4625+
//"shader" is the texture of the surf
45994626
MD5EXPECT ("shader");
46004627
// MD5 violation: the skin is a single material. adding prefixes/postfixes here is the wrong thing to do.
46014628
// but we do so anyway, because rerelease compat.
@@ -4700,34 +4727,35 @@ static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer)
47004727
surf->fbtextures[surf->numskins][2] = surf->fbtextures[surf->numskins][0];
47014728
}
47024729
}
4703-
4730+
// MD5 have only 1 pose, a.k.a frame in MDL / MD3
4731+
// because it uses skeletal animation instead of displaying/interpolating different frames/poses of vertices
47044732
surf->numposes = 1;
47054733

47064734
buffer = COM_Parse (buffer);
47074735
MD5EXPECT ("numverts");
47084736
surf->numverts_vbo = surf->numverts = MD5UINT ();
47094737

4710-
vinfo = (md5vertinfo_t *)Mem_Realloc (vinfo, sizeof (*vinfo) * (vertex_offset + surf->numverts));
4711-
poutvertexes = (md5vert_t *)Mem_Realloc (poutvertexes, sizeof (*poutvertexes) * (vertex_offset + surf->numverts));
4712-
total_numverts += surf->numverts;
4738+
md5vertinfo_t *vinfo = (md5vertinfo_t *)Mem_Alloc (sizeof (*vinfo) * surf->numverts);
4739+
md5vert_t *poutvertexes = (md5vert_t *)Mem_Alloc (sizeof (*poutvertexes) * surf->numverts);
4740+
47134741
while (MD5CHECK ("vert"))
47144742
{
47154743
size_t idx = MD5UINT ();
47164744
if (idx >= (size_t)surf->numverts)
47174745
Sys_Error ("vertex index out of bounds");
47184746
MD5EXPECT ("(");
4719-
poutvertexes[vertex_offset + idx].st[0] = MD5FLOAT ();
4720-
poutvertexes[vertex_offset + idx].st[1] = MD5FLOAT ();
4747+
poutvertexes[idx].st[0] = MD5FLOAT ();
4748+
poutvertexes[idx].st[1] = MD5FLOAT ();
47214749
MD5EXPECT (")");
4722-
vinfo[vertex_offset + idx].firstweight = MD5UINT () + weight_offset; // shift firstwieight by numwieigts of previous meshes
4723-
vinfo[vertex_offset + idx].count = MD5UINT ();
4750+
vinfo[idx].firstweight = MD5UINT ();
4751+
vinfo[idx].count = MD5UINT ();
47244752
}
47254753

47264754
MD5EXPECT ("numtris");
47274755
surf->numtris = MD5UINT ();
47284756
surf->numindexes = surf->numtris * 3;
4729-
poutindexes = (unsigned short *)Mem_Realloc (poutindexes, sizeof (*poutindexes) * (index_offset + surf->numindexes));
4730-
outhdr->total_numindexes += surf->numindexes;
4757+
unsigned short *poutindexes = (unsigned short *)Mem_Alloc (sizeof (unsigned short) * (surf->numindexes));
4758+
47314759
while (MD5CHECK ("tri"))
47324760
{
47334761
size_t idx = MD5UINT ();
@@ -4739,92 +4767,72 @@ static void Mod_LoadMD5MeshModel (qmodel_t *mod, const void *buffer)
47394767
size_t t = MD5UINT ();
47404768
if (t > (size_t)surf->numverts)
47414769
Sys_Error ("vertex index out of bounds");
4742-
poutindexes[index_offset + idx + j] = t + vertex_offset; // shift indices by numvertindexes of previous meshes
4770+
poutindexes[idx + j] = t;
47434771
}
47444772
}
47454773

47464774
// md5 is a gpu-unfriendly interchange format. :(
47474775
MD5EXPECT ("numweights");
4748-
numweights = MD5UINT ();
4749-
weight = (md5weightinfo_t *)Mem_Realloc (weight, sizeof (*weight) * (weight_offset + numweights));
4750-
total_numweights += numweights;
4776+
size_t numweights = MD5UINT ();
4777+
md5weightinfo_t *weight = (md5weightinfo_t *)Mem_Alloc (sizeof (*weight) * numweights);
47514778

47524779
while (MD5CHECK ("weight"))
47534780
{
47544781
size_t idx = MD5UINT ();
47554782
if (idx >= numweights)
47564783
Sys_Error ("weight index out of bounds");
47574784

4758-
weight[weight_offset + idx].joint_index = MD5UINT ();
4759-
if (weight[weight_offset + idx].joint_index >= numjoints)
4785+
weight[idx].joint_index = MD5UINT ();
4786+
if (weight[idx].joint_index >= numjoints)
47604787
Sys_Error ("joint index out of bounds");
4761-
weight[weight_offset + idx].pos[3] = MD5FLOAT ();
4788+
weight[idx].pos[3] = MD5FLOAT ();
47624789
MD5EXPECT ("(");
4763-
weight[weight_offset + idx].pos[0] = MD5FLOAT () * weight[weight_offset + idx].pos[3];
4764-
weight[weight_offset + idx].pos[1] = MD5FLOAT () * weight[weight_offset + idx].pos[3];
4765-
weight[weight_offset + idx].pos[2] = MD5FLOAT () * weight[weight_offset + idx].pos[3];
4790+
weight[idx].pos[0] = MD5FLOAT () * weight[idx].pos[3];
4791+
weight[idx].pos[1] = MD5FLOAT () * weight[idx].pos[3];
4792+
weight[idx].pos[2] = MD5FLOAT () * weight[idx].pos[3];
47664793
MD5EXPECT (")");
47674794
}
47684795

47694796
MD5EXPECT ("}");
47704797

4771-
// increment offsets for the next meshes
4772-
vertex_offset += surf->numverts;
4773-
index_offset += surf->numindexes;
4774-
weight_offset += numweights;
4798+
// so make it gpu-friendly.
4799+
MD5_BakeInfluences (fname, joint_poses, poutvertexes, vinfo, weight, surf->numverts, numweights);
4800+
// and now make up the normals that the format lacks. we'll still probably have issues from seams, but then so did qme, so at least its faithful...
4801+
// :P
4802+
MD5_ComputeNormals (poutvertexes, surf->numverts, poutindexes, surf->numindexes);
47754803

4776-
} // end foreach mesh
4804+
Mem_Free (weight);
4805+
Mem_Free (vinfo);
47774806

4778-
// vertex indices are 16 bit (VK_INDEX_TYPE_UINT16) so we cannot address more than MAXALIASVERTS vertexes.
4779-
if (total_numverts > MAXALIASVERTS)
4780-
Sys_Error ("MD5 model %s has too many vertices (%d; max = %d)", mod->name, (int)total_numverts, MAXALIASVERTS);
4807+
// Upload to GPU that surface/mesh m:
4808+
GLMesh_UploadBuffers (mod, surf, poutindexes, (byte *)poutvertexes, NULL, inverted_joints);
47814809

4782-
// so make it gpu-friendly.
4783-
MD5_BakeInfluences (fname, joint_poses, poutvertexes, vinfo, weight, total_numverts, total_numweights);
4784-
// and now make up the normals that the format lacks. we'll still probably have issues from seams, but then so did qme, so at least its faithful...
4785-
// :P
4786-
MD5_ComputeNormals (poutvertexes, total_numverts, poutindexes, outhdr->total_numindexes);
4810+
// concat surface vertices to total_vertexes
4811+
total_vertexes = (md5vert_t *)Mem_Realloc (total_vertexes, sizeof (*poutvertexes) * (total_numverts + surf->numverts));
4812+
memcpy ((void *)(total_vertexes + total_numverts), (const void *)poutvertexes, sizeof (*poutvertexes) * surf->numverts);
4813+
total_numverts += surf->numverts;
47874814

4788-
Mem_Free (weight);
4789-
Mem_Free (vinfo);
4815+
Mem_Free (poutvertexes);
4816+
Mem_Free (poutindexes);
47904817

4791-
TEMP_ALLOC_ZEROED (jointpose_t, inverted_joints, anim.numjoints * anim.numposes);
4792-
TEMP_ALLOC_ZEROED (jointpose_t, concat_joints, anim.numjoints);
4793-
for (size_t pose_index = 0; pose_index < anim.numposes; ++pose_index)
4794-
{
4795-
const jointpose_t *in_pose = anim.posedata + (pose_index * anim.numjoints);
4796-
const jointpose_t *out_pose = inverted_joints + (pose_index * anim.numjoints);
4797-
for (size_t joint_index = 0; joint_index < anim.numjoints; ++joint_index)
4798-
{
4799-
// concat it onto the parent (relative->abs)
4800-
if (joint_infos[joint_index].parent < 0)
4801-
memcpy (concat_joints[joint_index].mat, in_pose[joint_index].mat, sizeof (jointpose_t));
4802-
else
4803-
R_ConcatTransforms (
4804-
(void *)concat_joints[joint_infos[joint_index].parent].mat, (void *)in_pose[joint_index].mat, (void *)concat_joints[joint_index].mat);
4805-
// and finally invert it
4806-
R_ConcatTransforms ((void *)concat_joints[joint_index].mat, (void *)joint_infos[joint_index].inverse.mat, (void *)out_pose[joint_index].mat);
4807-
}
4808-
}
4809-
Mem_Free (anim.posedata);
4810-
4811-
GLMesh_UploadBuffers (mod, outhdr, poutindexes, (byte *)poutvertexes, NULL, inverted_joints);
4812-
TEMP_FREE (concat_joints);
4813-
TEMP_FREE (inverted_joints);
4818+
} // end foreach mesh
48144819

4815-
// the md5 format does not have its own modelflags, yet we still need to know about trails and rotating etc
4820+
// the MD5 format does not have its own modelflags, yet we still need to know about trails and rotating etc
48164821
mod->flags = MD5_HackyModelFlags (mod->name);
48174822

48184823
mod->synctype = ST_FRAMETIME; // keep MD5 animations synced to when .frame is changed. framegroups are otherwise not very useful.
48194824
mod->type = mod_alias;
48204825
mod->extradata[PV_MD5] = (byte *)outhdr;
48214826

4822-
Mod_CalcAliasBounds (mod, outhdr, total_numverts, (byte *)poutvertexes); // johnfitz
4827+
Mod_CalcAliasBounds (mod, outhdr, total_numverts, (byte *)total_vertexes); // johnfitz
4828+
4829+
Mem_Free (total_vertexes);
4830+
4831+
TEMP_FREE (concat_joints);
4832+
TEMP_FREE (inverted_joints);
48234833

48244834
TEMP_FREE (joint_poses);
48254835
TEMP_FREE (joint_infos)
4826-
Mem_Free (poutvertexes);
4827-
Mem_Free (poutindexes);
48284836
}
48294837

48304838
//=============================================================================

Quake/gl_model.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,6 @@ typedef struct aliashdr_s
344344
int flags;
345345
float size;
346346
int numindexes;
347-
// total nunumindexes in index_buffer, made of the union of this aliashdr_t and all of its nextsurface indexes.
348-
int total_numindexes;
349347
int numverts_vbo;
350348
int numposes;
351349
aliashdr_t *nextsurface; // spike

Quake/glquake.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ void R_DrawParticles_ShowTris (cb_context_t *cbx);
598598
void R_DrawSpriteModel_ShowTris (cb_context_t *cbx, entity_t *e);
599599

600600
void DrawGLPoly (cb_context_t *cbx, glpoly_t *p, float color[3], float alpha);
601-
void GLMesh_DeleteMeshBuffers (aliashdr_t *hdr);
601+
void GLMesh_DeleteMeshBuffers (aliashdr_t *mainhdr);
602602
void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr);
603603

604604
void Sky_Init (void);

0 commit comments

Comments
 (0)