Alias model VBO renderer: restructure so each model is stored in a separate VBO and load them in Mod_LoadAliasModel, rather than R_NewMap.

Initially I thought that we would never need to draw an alias model that hadn't been precached when R_NewMap runs, but this assumption turned out to be incorrect. This fixes the issue where progs/bolt.mdl wasn't rendering in the Scourge Done Slick demos.

git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1253 af15c1b1-3010-417e-b628-4374ebc0bcbd
This commit is contained in:
Eric Wasylishen 2015-09-20 20:10:49 +00:00
parent b2465a4c82
commit e007116a5d
9 changed files with 170 additions and 130 deletions

View file

@ -287,7 +287,8 @@ void BuildTris (void)
alltris += pheader->numtris;
}
void GL_MakeAliasModelDisplayLists_VBO (void);
static void GL_MakeAliasModelDisplayLists_VBO (void);
static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr);
/*
================
@ -436,144 +437,178 @@ void GL_MakeAliasModelDisplayLists_VBO (void)
}
}
}
// upload immediately
GLMesh_LoadVertexBuffer (aliasmodel, pheader);
}
#define NUMVERTEXNORMALS 162
extern float r_avertexnormals[NUMVERTEXNORMALS][3];
GLuint r_meshvbo = 0;
GLuint r_meshindexesvbo = 0;
/*
================
GLMesh_LoadVertexBuffers
GLMesh_LoadVertexBuffer
Loop over all precached alias models, and upload them into one big VBO plus
an GL_ELEMENT_ARRAY_BUFFER for the vertex indices.
Upload the given alias model's mesh to a VBO
Original code by MH from RMQEngine
================
*/
void GLMesh_LoadVertexBuffers (void)
static void GLMesh_LoadVertexBuffer (qmodel_t *m, const aliashdr_t *hdr)
{
int j;
qmodel_t *m;
int totalindexes = 0;
int totalvbosize = 0;
const aliasmesh_t *desc;
const short *indexes;
const trivertx_t *trivertexes;
byte *vbodata;
int f;
if (!gl_glsl_alias_able)
return;
// count the sizes we need
// ericw -- RMQEngine stored these vbo*ofs values in aliashdr_t, but we must not
// mutate Mod_Extradata since it might be reloaded from disk, so I moved them to qmodel_t
// (test case: roman1.bsp from arwop, 64mb heap)
m->vboindexofs = 0;
m->vboxyzofs = 0;
totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
m->vbostofs = totalvbosize;
totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t));
if (!hdr->numindexes) return;
if (!totalvbosize) return;
// grab the pointers to data in the extradata
// pass 1 - count the sizes we need
for (j = 1; j < MAX_MODELS; j++)
desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc);
indexes = (short *) ((byte *) hdr + hdr->indexes);
trivertexes = (trivertx_t *) ((byte *)hdr + hdr->vertexes);
// upload indices buffer
GL_DeleteBuffersFunc (1, &m->meshindexesvbo);
GL_GenBuffersFunc (1, &m->meshindexesvbo);
GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, m->meshindexesvbo);
GL_BufferDataFunc (GL_ELEMENT_ARRAY_BUFFER, hdr->numindexes * sizeof (unsigned short), indexes, GL_STATIC_DRAW);
// create the vertex buffer (empty)
vbodata = (byte *) malloc(totalvbosize);
memset(vbodata, 0, totalvbosize);
// fill in the vertices at the start of the buffer
for (f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
{
aliashdr_t *hdr;
int v;
meshxyz_t *xyz = (meshxyz_t *) (vbodata + (f * hdr->numverts_vbo * sizeof (meshxyz_t)));
const trivertx_t *tv = trivertexes + (hdr->numverts * f);
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
for (v = 0; v < hdr->numverts_vbo; v++)
{
trivertx_t trivert = tv[desc[v].vertindex];
hdr = (aliashdr_t *) Mod_Extradata (m);
xyz[v].xyz[0] = trivert.v[0];
xyz[v].xyz[1] = trivert.v[1];
xyz[v].xyz[2] = trivert.v[2];
xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression
// ericw -- RMQEngine stored these vbo*ofs values in aliashdr_t, but we must not
// mutate Mod_Extradata since it might be reloaded from disk, so I moved them to qmodel_t
// (test case: roman1.bsp from arwop, 64mb heap)
m->vboindexofs = (totalindexes * sizeof (unsigned short));
totalindexes += hdr->numindexes;
m->vboxyzofs = totalvbosize;
totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
m->vbostofs = totalvbosize;
totalvbosize += (hdr->numverts_vbo * sizeof (meshst_t));
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
xyz[v].normal[0] = 127 * r_avertexnormals[trivert.lightnormalindex][0];
xyz[v].normal[1] = 127 * r_avertexnormals[trivert.lightnormalindex][1];
xyz[v].normal[2] = 127 * r_avertexnormals[trivert.lightnormalindex][2];
xyz[v].normal[3] = 0; // unused; for 4-byte alignment
}
}
if (!totalindexes) return;
if (!totalvbosize) return;
// pass 2 - create the buffers
GL_DeleteBuffersFunc (1, &r_meshindexesvbo);
GL_GenBuffersFunc (1, &r_meshindexesvbo);
GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, r_meshindexesvbo);
GL_BufferDataFunc (GL_ELEMENT_ARRAY_BUFFER, totalindexes * sizeof (unsigned short), NULL, GL_STATIC_DRAW);
GL_DeleteBuffersFunc (1, &r_meshvbo);
GL_GenBuffersFunc (1, &r_meshvbo);
GL_BindBufferFunc (GL_ARRAY_BUFFER, r_meshvbo);
GL_BufferDataFunc (GL_ARRAY_BUFFER, totalvbosize, NULL, GL_STATIC_DRAW);
// pass 3 - fill in the buffers
for (j = 1; j < MAX_MODELS; j++)
// fill in the ST coords at the end of the buffer
{
int f;
aliashdr_t *hdr;
aliasmesh_t *desc;
meshst_t *st;
float hscale, vscale;
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
hdr = (aliashdr_t *) Mod_Extradata (m);
desc = (aliasmesh_t *) ((byte *) hdr + hdr->meshdesc);
//johnfitz -- padded skins
hscale = (float)hdr->skinwidth/(float)TexMgr_PadConditional(hdr->skinwidth);
vscale = (float)hdr->skinheight/(float)TexMgr_PadConditional(hdr->skinheight);
//johnfitz
GL_BufferSubDataFunc (GL_ELEMENT_ARRAY_BUFFER,
m->vboindexofs,
hdr->numindexes * sizeof (unsigned short),
((byte *) hdr + hdr->indexes));
for (f = 0; f < hdr->numposes; f++) // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm
{
int v;
meshxyz_t *xyz = (meshxyz_t *) malloc (hdr->numverts_vbo * sizeof (meshxyz_t));
trivertx_t *tv = (trivertx_t *) ((byte *) hdr + hdr->vertexes + (hdr->numverts * sizeof(trivertx_t) * f));
for (v = 0; v < hdr->numverts_vbo; v++)
{
trivertx_t trivert = tv[desc[v].vertindex];
xyz[v].xyz[0] = trivert.v[0];
xyz[v].xyz[1] = trivert.v[1];
xyz[v].xyz[2] = trivert.v[2];
xyz[v].xyz[3] = 1; // need w 1 for 4 byte vertex compression
// map the normal coordinates in [-1..1] to [-127..127] and store in an unsigned char.
// this introduces some error (less than 0.004), but the normals were very coarse
// to begin with
xyz[v].normal[0] = 127 * r_avertexnormals[trivert.lightnormalindex][0];
xyz[v].normal[1] = 127 * r_avertexnormals[trivert.lightnormalindex][1];
xyz[v].normal[2] = 127 * r_avertexnormals[trivert.lightnormalindex][2];
xyz[v].normal[3] = 0; // unused; for 4-byte alignment
}
GL_BufferSubDataFunc (GL_ARRAY_BUFFER,
m->vboxyzofs + (f * hdr->numverts_vbo * sizeof (meshxyz_t)),
hdr->numverts_vbo * sizeof (meshxyz_t),
xyz);
free (xyz);
}
st = (meshst_t *) malloc (hdr->numverts_vbo * sizeof (meshst_t));
st = (meshst_t *) (vbodata + m->vbostofs);
for (f = 0; f < hdr->numverts_vbo; f++)
{
st[f].st[0] = hscale * ((float) desc[f].st[0] + 0.5f) / (float) hdr->skinwidth;
st[f].st[1] = vscale * ((float) desc[f].st[1] + 0.5f) / (float) hdr->skinheight;
}
GL_BufferSubDataFunc (GL_ARRAY_BUFFER,
m->vbostofs,
hdr->numverts_vbo * sizeof (meshst_t),
st);
free (st);
}
// upload vertexes buffer
GL_DeleteBuffersFunc (1, &m->meshvbo);
GL_GenBuffersFunc (1, &m->meshvbo);
GL_BindBufferFunc (GL_ARRAY_BUFFER, m->meshvbo);
GL_BufferDataFunc (GL_ARRAY_BUFFER, totalvbosize, vbodata, GL_STATIC_DRAW);
free (vbodata);
// invalidate the cached bindings
GL_ClearBufferBindings ();
}
/*
================
GLMesh_LoadVertexBuffers
Loop over all precached alias models, and upload each one to a VBO.
================
*/
void GLMesh_LoadVertexBuffers (void)
{
int j;
qmodel_t *m;
const aliashdr_t *hdr;
if (!gl_glsl_alias_able)
return;
for (j = 1; j < MAX_MODELS; j++)
{
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
hdr = (const aliashdr_t *) Mod_Extradata (m);
GLMesh_LoadVertexBuffer (m, hdr);
}
}
/*
================
GLMesh_DeleteVertexBuffers
Delete VBOs for all loaded alias models
================
*/
void GLMesh_DeleteVertexBuffers (void)
{
int j;
qmodel_t *m;
if (!gl_glsl_alias_able)
return;
for (j = 1; j < MAX_MODELS; j++)
{
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
GL_DeleteBuffersFunc (1, &m->meshvbo);
m->meshvbo = 0;
GL_DeleteBuffersFunc (1, &m->meshindexesvbo);
m->meshindexesvbo = 0;
}
GL_ClearBufferBindings ();
}

View file

@ -200,6 +200,9 @@ void Mod_ResetAll (void)
int i;
qmodel_t *mod;
//ericw -- free alias model VBOs
GLMesh_DeleteVertexBuffers ();
for (i=0 , mod=mod_known ; i<mod_numknown ; i++, mod++)
{
if (!mod->needload) //otherwise Mod_ClearAll() did it already

View file

@ -485,6 +485,8 @@ typedef struct qmodel_s
// alias model
//
GLuint meshvbo;
GLuint meshindexesvbo;
int vboindexofs; // offset in vbo of the hdr->numindexes unsigned shorts
int vboxyzofs; // offset in vbo of hdr->numposes*hdr->numverts_vbo meshxyz_t
int vbostofs; // offset in vbo of hdr->numverts_vbo meshst_t

View file

@ -397,9 +397,9 @@ void R_NewMap (void)
R_ClearParticles ();
GL_BuildLightmaps ();
GL_BuildVBOs ();
GLMesh_LoadVertexBuffers ();
GL_BuildBModelVertexBuffer ();
//ericw -- no longer load alias models into a VBO here, it's done in Mod_LoadAliasModel
r_framecount = 0; //johnfitz -- paranoid?
r_visframecount = 0; //johnfitz -- paranoid?

View file

@ -737,14 +737,18 @@ static void VID_Restart (void)
return;
}
// ericw -- depending on platform / SDL version, after a video mode change we
// can have a new context (all textures are already freed) or the same context
// as before, in which case we need to delete the old textures to avoid a
// memory leak.
// ericw -- OS X, SDL1: textures, VBO's invalid after mode change
// OS X, SDL2: still valid after mode change
// To handle both cases, delete all GL objects (textures, VBO, GLSL) now.
// We must not interleave deleting the old objects with creating new ones, because
// one of the new objects could be given the same ID as an invalid handle
// which is later deleted.
TexMgr_DeleteTextureObjects ();
GLSLGamma_DeleteTexture ();
R_DeleteShaders ();
GL_DeleteBModelVertexBuffer ();
GLMesh_DeleteVertexBuffers ();
//
// set new mode
@ -753,7 +757,7 @@ static void VID_Restart (void)
GL_Init ();
TexMgr_ReloadImages ();
GL_BuildVBOs ();
GL_BuildBModelVertexBuffer ();
GLMesh_LoadVertexBuffers ();
GL_SetupState ();

View file

@ -343,8 +343,10 @@ void R_DrawTextureChains_Water (qmodel_t *model, entity_t *ent, texchain_t chain
void R_RenderDlights (void);
void GL_BuildLightmaps (void);
void GL_BuildVBOs (void);
void GL_DeleteBModelVertexBuffer (void);
void GL_BuildBModelVertexBuffer (void);
void GLMesh_LoadVertexBuffers (void);
void GLMesh_DeleteVertexBuffers (void);
void R_RebuildAllLightmaps (void);
int R_LightPoint (vec3_t p);

View file

@ -85,9 +85,6 @@ static const GLint pose2VertexAttrIndex = 2;
static const GLint pose2NormalAttrIndex = 3;
static const GLint texCoordsAttrIndex = 4;
extern GLuint r_meshvbo;
extern GLuint r_meshindexesvbo;
/*
=============
GLARB_GetXYZOffset
@ -237,8 +234,8 @@ void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata, gltextu
GL_UseProgramFunc (r_alias_program);
GL_BindBuffer (GL_ARRAY_BUFFER, r_meshvbo);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, r_meshindexesvbo);
GL_BindBuffer (GL_ARRAY_BUFFER, currententity->model->meshvbo);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, currententity->model->meshindexesvbo);
GL_EnableVertexAttribArrayFunc (texCoordsAttrIndex);
GL_EnableVertexAttribArrayFunc (pose1VertexAttrIndex);

View file

@ -961,15 +961,26 @@ void GL_BuildLightmaps (void)
GLuint gl_bmodel_vbo = 0;
void GL_DeleteBModelVertexBuffer (void)
{
if (!(gl_vbo_able && gl_mtexable && gl_max_texture_units >= 3))
return;
GL_DeleteBuffersFunc (1, &gl_bmodel_vbo);
gl_bmodel_vbo = 0;
GL_ClearBufferBindings ();
}
/*
==================
GL_BuildVBOs
GL_BuildBModelVertexBuffer
Deletes gl_bmodel_vbo if it already exists, then rebuilds it with all
surfaces from world + all brush models
==================
*/
void GL_BuildVBOs (void)
void GL_BuildBModelVertexBuffer (void)
{
unsigned int numverts, varray_bytes, varray_index;
int i, j;

View file

@ -804,20 +804,6 @@ void Cache_Flush (void)
Cache_Free ( cache_head.next->user, true); // reclaim the space //johnfitz -- added second argument
}
/*
============
Cache_FlushReload
ericw -- "flush" command is used by modders to view modified mdl files. need to
call GLMesh_LoadVertexBuffers to keep that use case working with GLSL renderer.
============
*/
static void Cache_FlushReload (void)
{
Cache_Flush ();
GLMesh_LoadVertexBuffers ();
}
/*
============
Cache_Print
@ -856,7 +842,7 @@ void Cache_Init (void)
cache_head.next = cache_head.prev = &cache_head;
cache_head.lru_next = cache_head.lru_prev = &cache_head;
Cmd_AddCommand ("flush", Cache_FlushReload);
Cmd_AddCommand ("flush", Cache_Flush);
}
/*