Alias model rendering fast-path using OpenGL 2.

In GL_MakeAliasModelDisplayLists, after saving the standerd triangle strips and fans onto the hunk, if the system is GL2 capable, we also save a second version of the mdl on the hunk designed to be loaded into a VBO (GL_MakeAliasModelDisplayLists_VBO). In R_NewMap, and on video mode changes, we call GLMesh_LoadVertexBuffers which loops over all precached mdl's and loads the data into a pair of VBO's (vertices and vertex indices).

Finally, in R_DrawAliasModel, assuming no rendering options are disabling the fast-path (r_drawflat 1, r_lightmap 1, or r_fullbright 1 would disable it), we call GL_DrawAliasFrame_GLSL, which sets up all of the bindings and draws the (possibly lerped) mdl in one glDrawElements call.

Special thanks to MH for some of the code from RMQEngine and the general concept.

git-svn-id: svn://svn.code.sf.net/p/quakespasm/code/trunk/quakespasm@1151 af15c1b1-3010-417e-b628-4374ebc0bcbd
This commit is contained in:
Eric Wasylishen 2015-01-20 18:59:15 +00:00
parent f4768566a1
commit 112c8b68b8
9 changed files with 939 additions and 24 deletions

View file

@ -287,6 +287,7 @@ void BuildTris (void)
alltris += pheader->numtris;
}
void GL_MakeAliasModelDisplayLists_VBO (void);
/*
================
@ -346,5 +347,233 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr)
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<numorder ; j++)
*verts++ = poseverts[i][vertexorder[j]];
// ericw
GL_MakeAliasModelDisplayLists_VBO ();
}
unsigned int r_meshindexbuffer = 0;
unsigned int r_meshvertexbuffer = 0;
/*
================
GL_MakeAliasModelDisplayLists_VBO
Saves data needed to build the VBO for this model on the hunk. Afterwards this
is copied to Mod_Extradata.
Original code by MH from RMQEngine
================
*/
void GL_MakeAliasModelDisplayLists_VBO (void)
{
int i, j;
int maxverts_vbo;
trivertx_t *verts;
unsigned short *indexes;
aliasmesh_t *desc;
if (!GLAlias_SupportsShaders())
return;
// first, copy the verts onto the hunk
verts = (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->numverts * sizeof(trivertx_t));
paliashdr->vertexes = (byte *)verts - (byte *)paliashdr;
for (i=0 ; i<paliashdr->numposes ; i++)
for (j=0 ; j<paliashdr->numverts ; j++)
verts[i*paliashdr->numverts + j] = poseverts[i][j];
// there can never be more than this number of verts and we just put them all on the hunk
maxverts_vbo = pheader->numtris * 3;
desc = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * maxverts_vbo);
// there will always be this number of indexes
indexes = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * maxverts_vbo);
pheader->indexes = (intptr_t) indexes - (intptr_t) pheader;
pheader->meshdesc = (intptr_t) desc - (intptr_t) pheader;
pheader->numindexes = 0;
pheader->numverts_vbo = 0;
for (i = 0; i < pheader->numtris; i++)
{
for (j = 0; j < 3; j++)
{
int v;
// index into hdr->vertexes
unsigned short vertindex = triangles[i].vertindex[j];
// basic s/t coords
int s = stverts[vertindex].s;
int t = stverts[vertindex].t;
// check for back side and adjust texcoord s
if (!triangles[i].facesfront && stverts[vertindex].onseam) s += pheader->skinwidth / 2;
// see does this vert already exist
for (v = 0; v < pheader->numverts_vbo; v++)
{
// it could use the same xyz but have different s and t
if (desc[v].vertindex == vertindex && (int) desc[v].st[0] == s && (int) desc[v].st[1] == t)
{
// exists; emit an index for it
indexes[pheader->numindexes++] = v;
// no need to check any more
break;
}
}
if (v == pheader->numverts_vbo)
{
// doesn't exist; emit a new vert and index
indexes[pheader->numindexes++] = pheader->numverts_vbo;
desc[pheader->numverts_vbo].vertindex = vertindex;
desc[pheader->numverts_vbo].st[0] = s;
desc[pheader->numverts_vbo++].st[1] = t;
}
}
}
}
#define NUMVERTEXNORMALS 162
extern float r_avertexnormals[NUMVERTEXNORMALS][3];
GLuint r_meshvbo = 0;
GLuint r_meshindexesvbo = 0;
/*
================
GLMesh_LoadVertexBuffers
Loop over all precached alias models, and upload them into one big VBO plus
an GL_ELEMENT_ARRAY_BUFFER for the vertex indices.
Original code by MH from RMQEngine
================
*/
void GLMesh_LoadVertexBuffers (void)
{
int j;
qmodel_t *m;
int totalindexes = 0;
int totalvbosize = 0;
if (!GLAlias_SupportsShaders())
return;
// pass 1 - count the sizes we need
for (j = 1; j < MAX_MODELS; j++)
{
aliashdr_t *hdr;
if (!(m = cl.model_precache[j])) break;
if (m->type != mod_alias) continue;
hdr = Mod_Extradata (m);
// 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));
}
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++)
{
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 = 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));
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);
}
// invalidate the cached bindings
GL_ClearBufferBindings ();
}

View file

@ -282,6 +282,26 @@ Alias models are position independent, so the cache manager can move them.
==============================================================================
*/
//-- from RMQEngine
// split out to keep vertex sizes down
typedef struct aliasmesh_s
{
float st[2];
unsigned short vertindex;
} aliasmesh_t;
typedef struct meshxyz_s
{
byte xyz[4];
signed char normal[4];
} meshxyz_t;
typedef struct meshst_s
{
float st[2];
} meshst_t;
//--
typedef struct
{
int firstpose;
@ -332,6 +352,14 @@ typedef struct {
int flags;
float size;
//ericw -- used to populate vbo
int numverts_vbo; // number of verts with unique x,y,z,s,t
intptr_t meshdesc; // offset into extradata: numverts_vbo aliasmesh_t
int numindexes;
intptr_t indexes; // offset into extradata: numindexes unsigned shorts
intptr_t vertexes; // offset into extradata: numposes*vertsperframe trivertx_t
//ericw --
int numposes;
int poseverts;
int posedata; // numposes*poseverts trivert_t
@ -449,6 +477,14 @@ typedef struct qmodel_s
int bspversion;
//
// alias model
//
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
//
// additional model data
//

View file

@ -447,6 +447,11 @@ void R_SetupView (void)
//
//==============================================================================
static int R_EntitySortFunc(const void *a, const void *b)
{
return (*(entity_t **)a)->model - (*(entity_t **)b)->model;
}
/*
=============
R_DrawEntitiesOnList
@ -454,15 +459,21 @@ R_DrawEntitiesOnList
*/
void R_DrawEntitiesOnList (qboolean alphapass) //johnfitz -- added parameter
{
entity_t *entities_sorted[MAX_VISEDICTS];
int i;
if (!r_drawentities.value)
return;
//ericw -- draw the entities sorted by model, to eliminate redundant texture binding
memcpy (entities_sorted, cl_visedicts, cl_numvisedicts * sizeof(entity_t *));
qsort (entities_sorted, cl_numvisedicts, sizeof(entity_t *), R_EntitySortFunc);
//ericw --
//johnfitz -- sprites are not a special case
for (i=0 ; i<cl_numvisedicts ; i++)
{
currententity = cl_visedicts[i];
currententity = entities_sorted[i];
//johnfitz -- if alphapass is true, draw only alpha entites this time
//if alphapass is false, draw only nonalpha entities this time

View file

@ -274,6 +274,7 @@ void R_NewMap (void)
GL_BuildLightmaps ();
GL_BuildVBOs ();
GLMesh_LoadVertexBuffers ();
r_framecount = 0; //johnfitz -- paranoid?
r_visframecount = 0; //johnfitz -- paranoid?
@ -320,3 +321,185 @@ void R_TimeRefresh_f (void)
void D_FlushCaches (void)
{
}
static GLuint gl_programs[16];
static int gl_num_programs;
static qboolean GL_CheckShader (GLuint shader)
{
GLint status;
GL_GetShaderivFunc (shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE)
{
char infolog[1024];
memset(infolog, 0, sizeof(infolog));
GL_GetShaderInfoLogFunc (shader, sizeof(infolog), NULL, infolog);
Con_Warning ("GLSL program failed to compile: %s", infolog);
return false;
}
return true;
}
static qboolean GL_CheckProgram (GLuint program)
{
GLint status;
GL_GetProgramivFunc (program, GL_LINK_STATUS, &status);
if (status != GL_TRUE)
{
char infolog[1024];
memset(infolog, 0, sizeof(infolog));
GL_GetProgramInfoLogFunc (program, sizeof(infolog), NULL, infolog);
Con_Warning ("GLSL program failed to link: %s", infolog);
return false;
}
return true;
}
/*
====================
GL_CreateProgram
Compiles and returns GLSL program.
====================
*/
GLuint GL_CreateProgram (const GLchar *vertSource, const GLchar *fragSource, int numbindings, const glsl_attrib_binding_t *bindings)
{
int i;
GLuint program, vertShader, fragShader;
if (!GLAlias_SupportsShaders())
return 0;
vertShader = GL_CreateShaderFunc (GL_VERTEX_SHADER);
GL_ShaderSourceFunc (vertShader, 1, &vertSource, NULL);
GL_CompileShaderFunc (vertShader);
if (!GL_CheckShader (vertShader))
{
GL_DeleteShaderFunc (vertShader);
return 0;
}
fragShader = GL_CreateShaderFunc (GL_FRAGMENT_SHADER);
GL_ShaderSourceFunc (fragShader, 1, &fragSource, NULL);
GL_CompileShaderFunc (fragShader);
if (!GL_CheckShader (fragShader))
{
GL_DeleteShaderFunc (vertShader);
GL_DeleteShaderFunc (fragShader);
return 0;
}
program = GL_CreateProgramFunc ();
GL_AttachShaderFunc (program, vertShader);
GL_DeleteShaderFunc (vertShader);
GL_AttachShaderFunc (program, fragShader);
GL_DeleteShaderFunc (fragShader);
for (i = 0; i < numbindings; i++)
{
GL_BindAttribLocationFunc (program, bindings[i].attrib, bindings[i].name);
}
GL_LinkProgramFunc (program);
if (!GL_CheckProgram (program))
{
GL_DeleteProgramFunc (program);
return 0;
}
else
{
if (gl_num_programs == (sizeof(gl_programs)/sizeof(GLuint)))
Host_Error ("gl_programs overflow");
gl_programs[gl_num_programs] = program;
gl_num_programs++;
return program;
}
}
/*
====================
R_DeleteShaders
Deletes any GLSL programs that have been created.
====================
*/
void R_DeleteShaders (void)
{
int i;
if (!GLAlias_SupportsShaders())
return;
for (i = 0; i < gl_num_programs; i++)
{
GL_DeleteProgramFunc (gl_programs[i]);
gl_programs[i] = 0;
}
gl_num_programs = 0;
}
GLuint current_array_buffer, current_element_array_buffer;
/*
====================
GL_BindBuffer
glBindBuffer wrapper
====================
*/
void GL_BindBuffer (GLenum target, GLuint buffer)
{
GLuint *cache;
if (!gl_vbo_able)
return;
switch (target)
{
case GL_ARRAY_BUFFER:
cache = &current_array_buffer;
break;
case GL_ELEMENT_ARRAY_BUFFER:
cache = &current_element_array_buffer;
break;
default:
Host_Error("GL_BindBuffer: unsupported target %d", (int)target);
return;
}
if (*cache != buffer)
{
*cache = buffer;
GL_BindBufferFunc (target, *cache);
}
}
/*
====================
GL_ClearBufferBindings
This must be called if you do anything that could make the cached bindings
invalid (e.g. manually binding, destroying the context).
====================
*/
void GL_ClearBufferBindings ()
{
if (!gl_vbo_able)
return;
current_array_buffer = 0;
current_element_array_buffer = 0;
GL_BindBufferFunc (GL_ARRAY_BUFFER, 0);
GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, 0);
}

View file

@ -100,6 +100,7 @@ qboolean gl_anisotropy_able = false; //johnfitz
float gl_max_anisotropy; //johnfitz
qboolean gl_texture_NPOT = false; //ericw
qboolean gl_vbo_able = false; //ericw
qboolean gl_glsl_able = false; //ericw
GLint gl_max_texture_units = 0; //ericw
PFNGLMULTITEXCOORD2FARBPROC GL_MTexCoord2fFunc = NULL; //johnfitz
@ -107,9 +108,34 @@ PFNGLACTIVETEXTUREARBPROC GL_SelectTextureFunc = NULL; //johnfitz
PFNGLCLIENTACTIVETEXTUREARBPROC GL_ClientActiveTextureFunc = NULL; //ericw
PFNGLBINDBUFFERARBPROC GL_BindBufferFunc = NULL; //ericw
PFNGLBUFFERDATAARBPROC GL_BufferDataFunc = NULL; //ericw
PFNGLBUFFERSUBDATAARBPROC GL_BufferSubDataFunc = NULL; //ericw
PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc = NULL; //ericw
PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc = NULL; //ericw
QS_PFNGLCREATESHADERPROC GL_CreateShaderFunc = NULL; //ericw
QS_PFNGLDELETESHADERPROC GL_DeleteShaderFunc = NULL; //ericw
QS_PFNGLDELETEPROGRAMPROC GL_DeleteProgramFunc = NULL; //ericw
QS_PFNGLSHADERSOURCEPROC GL_ShaderSourceFunc = NULL; //ericw
QS_PFNGLCOMPILESHADERPROC GL_CompileShaderFunc = NULL; //ericw
QS_PFNGLGETSHADERIVPROC GL_GetShaderivFunc = NULL; //ericw
QS_PFNGLGETSHADERINFOLOGPROC GL_GetShaderInfoLogFunc = NULL; //ericw
QS_PFNGLGETPROGRAMIVPROC GL_GetProgramivFunc = NULL; //ericw
QS_PFNGLGETPROGRAMINFOLOGPROC GL_GetProgramInfoLogFunc = NULL; //ericw
QS_PFNGLCREATEPROGRAMPROC GL_CreateProgramFunc = NULL; //ericw
QS_PFNGLATTACHSHADERPROC GL_AttachShaderFunc = NULL; //ericw
QS_PFNGLLINKPROGRAMPROC GL_LinkProgramFunc = NULL; //ericw
QS_PFNGLBINDATTRIBLOCATIONFUNC GL_BindAttribLocationFunc = NULL; //ericw
QS_PFNGLUSEPROGRAMPROC GL_UseProgramFunc = NULL; //ericw
QS_PFNGLGETATTRIBLOCATIONPROC GL_GetAttribLocationFunc = NULL; //ericw
QS_PFNGLVERTEXATTRIBPOINTERPROC GL_VertexAttribPointerFunc = NULL; //ericw
QS_PFNGLENABLEVERTEXATTRIBARRAYPROC GL_EnableVertexAttribArrayFunc = NULL; //ericw
QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC GL_DisableVertexAttribArrayFunc = NULL; //ericw
QS_PFNGLGETUNIFORMLOCATIONPROC GL_GetUniformLocationFunc = NULL; //ericw
QS_PFNGLUNIFORM1IPROC GL_Uniform1iFunc = NULL; //ericw
QS_PFNGLUNIFORM1FPROC GL_Uniform1fFunc = NULL; //ericw
QS_PFNGLUNIFORM3FPROC GL_Uniform3fFunc = NULL; //ericw
QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc = NULL; //ericw
//====================================
//johnfitz -- new cvars
@ -659,6 +685,7 @@ static void VID_Restart (void)
GL_Init ();
TexMgr_ReloadImages ();
GL_BuildVBOs ();
GLMesh_LoadVertexBuffers ();
GL_SetupState ();
//warpimages needs to be recalculated
@ -824,9 +851,10 @@ static void GL_CheckExtensions (void)
{
GL_BindBufferFunc = (PFNGLBINDBUFFERARBPROC) SDL_GL_GetProcAddress("glBindBufferARB");
GL_BufferDataFunc = (PFNGLBUFFERDATAARBPROC) SDL_GL_GetProcAddress("glBufferDataARB");
GL_BufferSubDataFunc = (PFNGLBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glBufferSubDataARB");
GL_DeleteBuffersFunc = (PFNGLDELETEBUFFERSARBPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB");
GL_GenBuffersFunc = (PFNGLGENBUFFERSARBPROC) SDL_GL_GetProcAddress("glGenBuffersARB");
if (GL_BindBufferFunc && GL_BufferDataFunc && GL_DeleteBuffersFunc && GL_GenBuffersFunc)
if (GL_BindBufferFunc && GL_BufferDataFunc && GL_BufferSubDataFunc && GL_DeleteBuffersFunc && GL_GenBuffersFunc)
{
Con_Printf("FOUND: ARB_vertex_buffer_object\n");
gl_vbo_able = true;
@ -994,6 +1022,73 @@ static void GL_CheckExtensions (void)
{
Con_Warning ("texture_non_power_of_two not supported\n");
}
// GLSL
//
if (COM_CheckParm("-noglsl"))
Con_Warning ("GLSL disabled at command line\n");
else if (gl_version_major >= 2)
{
GL_CreateShaderFunc = (QS_PFNGLCREATESHADERPROC) SDL_GL_GetProcAddress("glCreateShader");
GL_DeleteShaderFunc = (QS_PFNGLDELETESHADERPROC) SDL_GL_GetProcAddress("glDeleteShader");
GL_DeleteProgramFunc = (QS_PFNGLDELETEPROGRAMPROC) SDL_GL_GetProcAddress("glDeleteProgram");
GL_ShaderSourceFunc = (QS_PFNGLSHADERSOURCEPROC) SDL_GL_GetProcAddress("glShaderSource");
GL_CompileShaderFunc = (QS_PFNGLCOMPILESHADERPROC) SDL_GL_GetProcAddress("glCompileShader");
GL_GetShaderivFunc = (QS_PFNGLGETSHADERIVPROC) SDL_GL_GetProcAddress("glGetShaderiv");
GL_GetShaderInfoLogFunc = (QS_PFNGLGETSHADERINFOLOGPROC) SDL_GL_GetProcAddress("glGetShaderInfoLog");
GL_GetProgramivFunc = (QS_PFNGLGETPROGRAMIVPROC) SDL_GL_GetProcAddress("glGetProgramiv");
GL_GetProgramInfoLogFunc = (QS_PFNGLGETPROGRAMINFOLOGPROC) SDL_GL_GetProcAddress("glGetProgramInfoLog");
GL_CreateProgramFunc = (QS_PFNGLCREATEPROGRAMPROC) SDL_GL_GetProcAddress("glCreateProgram");
GL_AttachShaderFunc = (QS_PFNGLATTACHSHADERPROC) SDL_GL_GetProcAddress("glAttachShader");
GL_LinkProgramFunc = (QS_PFNGLLINKPROGRAMPROC) SDL_GL_GetProcAddress("glLinkProgram");
GL_BindAttribLocationFunc = (QS_PFNGLBINDATTRIBLOCATIONFUNC) SDL_GL_GetProcAddress("glBindAttribLocation");
GL_UseProgramFunc = (QS_PFNGLUSEPROGRAMPROC) SDL_GL_GetProcAddress("glUseProgram");
GL_GetAttribLocationFunc = (QS_PFNGLGETATTRIBLOCATIONPROC) SDL_GL_GetProcAddress("glGetAttribLocation");
GL_VertexAttribPointerFunc = (QS_PFNGLVERTEXATTRIBPOINTERPROC) SDL_GL_GetProcAddress("glVertexAttribPointer");
GL_EnableVertexAttribArrayFunc = (QS_PFNGLENABLEVERTEXATTRIBARRAYPROC) SDL_GL_GetProcAddress("glEnableVertexAttribArray");
GL_DisableVertexAttribArrayFunc = (QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC) SDL_GL_GetProcAddress("glDisableVertexAttribArray");
GL_GetUniformLocationFunc = (QS_PFNGLGETUNIFORMLOCATIONPROC) SDL_GL_GetProcAddress("glGetUniformLocation");
GL_Uniform1iFunc = (QS_PFNGLUNIFORM1IPROC) SDL_GL_GetProcAddress("glUniform1i");
GL_Uniform1fFunc = (QS_PFNGLUNIFORM1FPROC) SDL_GL_GetProcAddress("glUniform1f");
GL_Uniform3fFunc = (QS_PFNGLUNIFORM3FPROC) SDL_GL_GetProcAddress("glUniform3f");
GL_Uniform4fFunc = (QS_PFNGLUNIFORM4FPROC) SDL_GL_GetProcAddress("glUniform4f");
if (GL_CreateShaderFunc &&
GL_DeleteShaderFunc &&
GL_DeleteProgramFunc &&
GL_ShaderSourceFunc &&
GL_CompileShaderFunc &&
GL_GetShaderivFunc &&
GL_GetShaderInfoLogFunc &&
GL_GetProgramivFunc &&
GL_GetProgramInfoLogFunc &&
GL_CreateProgramFunc &&
GL_AttachShaderFunc &&
GL_LinkProgramFunc &&
GL_BindAttribLocationFunc &&
GL_UseProgramFunc &&
GL_GetAttribLocationFunc &&
GL_VertexAttribPointerFunc &&
GL_EnableVertexAttribArrayFunc &&
GL_DisableVertexAttribArrayFunc &&
GL_GetUniformLocationFunc &&
GL_Uniform1iFunc &&
GL_Uniform1fFunc &&
GL_Uniform3fFunc &&
GL_Uniform4fFunc)
{
Con_Printf("FOUND: GLSL\n");
gl_glsl_able = true;
}
else
{
Con_Warning ("GLSL not available\n");
}
}
else
{
Con_Warning ("OpenGL version < 2, GLSL not available\n");
}
}
/*
@ -1065,6 +1160,10 @@ static void GL_Init (void)
Cbuf_AddText ("gl_clear 1");
}
//johnfitz
R_DeleteShaders ();
GLAlias_CreateShaders ();
GL_ClearBufferBindings ();
}
/*

View file

@ -165,11 +165,65 @@ extern qboolean gl_anisotropy_able;
//ericw -- VBO
extern PFNGLBINDBUFFERARBPROC GL_BindBufferFunc;
extern PFNGLBUFFERDATAARBPROC GL_BufferDataFunc;
extern PFNGLBUFFERSUBDATAARBPROC GL_BufferSubDataFunc;
extern PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc;
extern PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc;
extern qboolean gl_vbo_able;
//ericw
//ericw -- GLSL
// SDL 1.2 has a bug where it doesn't provide these typedefs on OS X!
typedef GLuint (APIENTRYP QS_PFNGLCREATESHADERPROC) (GLenum type);
typedef void (APIENTRYP QS_PFNGLDELETESHADERPROC) (GLuint shader);
typedef void (APIENTRYP QS_PFNGLDELETEPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP QS_PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length);
typedef void (APIENTRYP QS_PFNGLCOMPILESHADERPROC) (GLuint shader);
typedef void (APIENTRYP QS_PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params);
typedef void (APIENTRYP QS_PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef void (APIENTRYP QS_PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params);
typedef void (APIENTRYP QS_PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog);
typedef GLuint (APIENTRYP QS_PFNGLCREATEPROGRAMPROC) (void);
typedef void (APIENTRYP QS_PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
typedef void (APIENTRYP QS_PFNGLLINKPROGRAMPROC) (GLuint program);
typedef void (APIENTRYP QS_PFNGLBINDATTRIBLOCATIONFUNC) (GLuint program, GLuint index, const GLchar *name);
typedef void (APIENTRYP QS_PFNGLUSEPROGRAMPROC) (GLuint program);
typedef GLint (APIENTRYP QS_PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP QS_PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
typedef void (APIENTRYP QS_PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef void (APIENTRYP QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index);
typedef GLint (APIENTRYP QS_PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name);
typedef void (APIENTRYP QS_PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
typedef void (APIENTRYP QS_PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);
typedef void (APIENTRYP QS_PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
typedef void (APIENTRYP QS_PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
extern QS_PFNGLCREATESHADERPROC GL_CreateShaderFunc;
extern QS_PFNGLDELETESHADERPROC GL_DeleteShaderFunc;
extern QS_PFNGLDELETEPROGRAMPROC GL_DeleteProgramFunc;
extern QS_PFNGLSHADERSOURCEPROC GL_ShaderSourceFunc;
extern QS_PFNGLCOMPILESHADERPROC GL_CompileShaderFunc;
extern QS_PFNGLGETSHADERIVPROC GL_GetShaderivFunc;
extern QS_PFNGLGETSHADERINFOLOGPROC GL_GetShaderInfoLogFunc;
extern QS_PFNGLGETPROGRAMIVPROC GL_GetProgramivFunc;
extern QS_PFNGLGETPROGRAMINFOLOGPROC GL_GetProgramInfoLogFunc;
extern QS_PFNGLCREATEPROGRAMPROC GL_CreateProgramFunc;
extern QS_PFNGLATTACHSHADERPROC GL_AttachShaderFunc;
extern QS_PFNGLLINKPROGRAMPROC GL_LinkProgramFunc;
extern QS_PFNGLBINDATTRIBLOCATIONFUNC GL_BindAttribLocationFunc;
extern QS_PFNGLUSEPROGRAMPROC GL_UseProgramFunc;
extern QS_PFNGLGETATTRIBLOCATIONPROC GL_GetAttribLocationFunc;
extern QS_PFNGLVERTEXATTRIBPOINTERPROC GL_VertexAttribPointerFunc;
extern QS_PFNGLENABLEVERTEXATTRIBARRAYPROC GL_EnableVertexAttribArrayFunc;
extern QS_PFNGLDISABLEVERTEXATTRIBARRAYPROC GL_DisableVertexAttribArrayFunc;
extern QS_PFNGLGETUNIFORMLOCATIONPROC GL_GetUniformLocationFunc;
extern QS_PFNGLUNIFORM1IPROC GL_Uniform1iFunc;
extern QS_PFNGLUNIFORM1FPROC GL_Uniform1fFunc;
extern QS_PFNGLUNIFORM3FPROC GL_Uniform3fFunc;
extern QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc;
extern qboolean gl_glsl_able;
// ericw --
//ericw -- NPOT texture support
extern qboolean gl_texture_NPOT;
@ -233,6 +287,11 @@ extern int gl_warpimagesize; //johnfitz -- for water warp
extern qboolean r_drawflat_cheatsafe, r_fullbright_cheatsafe, r_lightmap_cheatsafe, r_drawworld_cheatsafe; //johnfitz
typedef struct glsl_attrib_binding_s {
const char *name;
GLuint attrib;
} glsl_attrib_binding_t;
//johnfitz -- fog functions called from outside gl_fog.c
void Fog_ParseServerMessage (void);
float *Fog_GetColor (void);
@ -275,6 +334,7 @@ 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 GLMesh_LoadVertexBuffers (void);
void R_RebuildAllLightmaps (void);
int R_LightPoint (vec3_t p);
@ -289,6 +349,11 @@ void R_DrawBrushModel_ShowTris (entity_t *e);
void R_DrawAliasModel_ShowTris (entity_t *e);
void R_DrawParticles_ShowTris (void);
GLuint GL_CreateProgram (const GLchar *vertSource, const GLchar *fragSource, int numbindings, const glsl_attrib_binding_t *bindings);
void R_DeleteShaders (void);
qboolean GLAlias_SupportsShaders (void);
void GLAlias_CreateShaders (void);
void GL_DrawAliasShadow (entity_t *e);
void DrawGLTriangleFan (glpoly_t *p);
void DrawGLPoly (glpoly_t *p);
@ -308,5 +373,7 @@ void R_ChainSurface (msurface_t *surf, texchain_t chain);
void R_DrawTextureChains (qmodel_t *model, entity_t *ent, texchain_t chain);
void R_DrawWorld_Water (void);
void GL_BindBuffer (GLenum target, GLuint buffer);
void GL_ClearBufferBindings ();
#endif /* __GLQUAKE_H */

View file

@ -50,7 +50,7 @@ float r_avertexnormal_dots[SHADEDOT_QUANT][256] =
extern vec3_t lightspot;
float *shadedots = r_avertexnormal_dots[0];
vec3_t shadevector;
float entalpha; //johnfitz
qboolean overbright; //johnfitz
@ -67,6 +67,256 @@ typedef struct {
} lerpdata_t;
//johnfitz
static GLuint r_alias_program;
// uniforms used in vert shader
static GLuint blendLoc;
static GLuint shadevectorLoc;
static GLuint lightColorLoc;
// uniforms used in frag shader
static GLuint texLoc;
static GLuint fullbrightTexLoc;
static GLuint useFullbrightTexLoc;
static GLuint useOverbrightLoc;
static const GLint pose1VertexAttrIndex = 0;
static const GLint pose1NormalAttrIndex = 1;
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
Returns the offset of the first vertex's meshxyz_t.xyz in the vbo for the given
model and pose.
=============
*/
static void *GLARB_GetXYZOffset (aliashdr_t *hdr, int pose)
{
meshxyz_t dummy;
int xyzoffs = ((char*)&dummy.xyz - (char*)&dummy);
return (void *) (currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + xyzoffs);
}
/*
=============
GLARB_GetNormalOffset
Returns the offset of the first vertex's meshxyz_t.normal in the vbo for the
given model and pose.
=============
*/
static void *GLARB_GetNormalOffset (aliashdr_t *hdr, int pose)
{
meshxyz_t dummy;
int normaloffs = ((char*)&dummy.normal - (char*)&dummy);
return (void *)(currententity->model->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + normaloffs);
}
/*
=============
GLAlias_SupportsShaders
Returns whether OpenGL has the capabilities we need for the shader path.
=============
*/
qboolean GLAlias_SupportsShaders (void)
{
return gl_glsl_able && gl_vbo_able && gl_max_texture_units >= 3;
}
/*
=============
GLAlias_GetUniformLocation
=============
*/
static GLint GLAlias_GetUniformLocation (const char *name)
{
GLint location;
location = GL_GetUniformLocationFunc(r_alias_program, name);
if (location == -1)
{
Con_Warning("GL_GetUniformLocationFunc %s failed", name);
r_alias_program = 0;
}
return location;
}
/*
=============
GLAlias_CreateShaders
=============
*/
void GLAlias_CreateShaders (void)
{
const glsl_attrib_binding_t bindings[] = {
{ "TexCoords", texCoordsAttrIndex },
{ "Pose1Vert", pose1VertexAttrIndex },
{ "Pose1Normal", pose1NormalAttrIndex },
{ "Pose2Vert", pose2VertexAttrIndex },
{ "Pose2Normal", pose2NormalAttrIndex }
};
const GLchar *vertSource = \
"#version 110\n"
"\n"
"uniform float Blend;\n"
"uniform vec3 ShadeVector;\n"
"uniform vec4 LightColor;\n"
"attribute vec4 TexCoords; // only xy are used \n"
"attribute vec4 Pose1Vert;\n"
"attribute vec3 Pose1Normal;\n"
"attribute vec4 Pose2Vert;\n"
"attribute vec3 Pose2Normal;\n"
"float r_avertexnormal_dot(vec3 vertexnormal) // from MH \n"
"{\n"
" float dot = dot(vertexnormal, ShadeVector);\n"
" // wtf - this reproduces anorm_dots within as reasonable a degree of tolerance as the >= 0 case\n"
" if (dot < 0.0)\n"
" return 1.0 + dot * (13.0 / 44.0);\n"
" else\n"
" return 1.0 + dot;\n"
"}\n"
"void main()\n"
"{\n"
" gl_TexCoord[0] = TexCoords;\n"
" vec4 lerpedVert = mix(Pose1Vert, Pose2Vert, Blend);\n"
" gl_Position = gl_ModelViewProjectionMatrix * lerpedVert;\n"
" float dot1 = r_avertexnormal_dot(Pose1Normal);\n"
" float dot2 = r_avertexnormal_dot(Pose2Normal);\n"
" gl_FrontColor = LightColor * vec4(vec3(mix(dot1, dot2, Blend)), 1.0);\n"
" // fog\n"
" vec3 ecPosition = vec3(gl_ModelViewMatrix * lerpedVert);\n"
" gl_FogFragCoord = abs(ecPosition.z);\n"
"}\n";
const GLchar *fragSource = \
"#version 110\n"
"\n"
"uniform sampler2D Tex;\n"
"uniform sampler2D FullbrightTex;\n"
"uniform bool UseFullbrightTex;\n"
"uniform bool UseOverbright;\n"
"void main()\n"
"{\n"
" vec4 result = texture2D(Tex, gl_TexCoord[0].xy);\n"
" result *= gl_Color;\n"
" if (UseOverbright)\n"
" result.rgb *= 2.0;\n"
" if (UseFullbrightTex)\n"
" result += texture2D(FullbrightTex, gl_TexCoord[0].xy);\n"
" result = clamp(result, 0.0, 1.0);\n"
" // apply GL_EXP2 fog (from the orange book)\n"
" float fog = exp(-gl_Fog.density * gl_Fog.density * gl_FogFragCoord * gl_FogFragCoord);\n"
" fog = clamp(fog, 0.0, 1.0);\n"
" result = vec4(mix(gl_Fog.color.rgb, result.rgb, fog), result.a);\n"
" gl_FragColor = result;\n"
"}\n";
if (!GLAlias_SupportsShaders())
return;
r_alias_program = GL_CreateProgram (vertSource, fragSource, sizeof(bindings)/sizeof(bindings[0]), bindings);
if (r_alias_program != 0)
{
// get uniform locations
blendLoc = GLAlias_GetUniformLocation ("Blend");
shadevectorLoc = GLAlias_GetUniformLocation ("ShadeVector");
lightColorLoc = GLAlias_GetUniformLocation ("LightColor");
texLoc = GLAlias_GetUniformLocation("Tex");
fullbrightTexLoc = GLAlias_GetUniformLocation("FullbrightTex");
useFullbrightTexLoc = GLAlias_GetUniformLocation("UseFullbrightTex");
useOverbrightLoc = GLAlias_GetUniformLocation("UseOverbright");
}
}
/*
=============
GL_DrawAliasFrame_GLSL -- ericw
Optimized alias model drawing codepath.
Compared to the original GL_DrawAliasFrame, this makes 1 draw call,
no vertex data is uploaded (it's already in the r_meshvbo and r_meshindexesvbo
static VBOs), and lerping and lighting is done in the vertex shader.
Supports optional overbright, optional fullbright pixels.
Based on code by MH from RMQEngine
=============
*/
void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata, gltexture_t *tx, gltexture_t *fb)
{
float blend;
if (lerpdata.pose1 != lerpdata.pose2)
{
blend = lerpdata.blend;
}
else // poses the same means either 1. the entity has paused its animation, or 2. r_lerpmodels is disabled
{
blend = 0;
}
GL_UseProgramFunc (r_alias_program);
GL_BindBuffer (GL_ARRAY_BUFFER, r_meshvbo);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, r_meshindexesvbo);
GL_EnableVertexAttribArrayFunc (texCoordsAttrIndex);
GL_EnableVertexAttribArrayFunc (pose1VertexAttrIndex);
GL_EnableVertexAttribArrayFunc (pose2VertexAttrIndex);
GL_EnableVertexAttribArrayFunc (pose1NormalAttrIndex);
GL_EnableVertexAttribArrayFunc (pose2NormalAttrIndex);
GL_VertexAttribPointerFunc (texCoordsAttrIndex, 2, GL_FLOAT, GL_FALSE, 0, (void *)(intptr_t)currententity->model->vbostofs);
GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose2));
// GL_TRUE to normalize the signed bytes to [-1 .. 1]
GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose1));
GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 4, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose2));
// set uniforms
GL_Uniform1fFunc (blendLoc, blend);
GL_Uniform3fFunc (shadevectorLoc, shadevector[0], shadevector[1], shadevector[2]);
GL_Uniform4fFunc (lightColorLoc, lightcolor[0], lightcolor[1], lightcolor[2], entalpha);
GL_Uniform1iFunc (texLoc, 0);
GL_Uniform1iFunc (fullbrightTexLoc, 1);
GL_Uniform1iFunc (useFullbrightTexLoc, (fb != NULL) ? 1 : 0);
GL_Uniform1fFunc (useOverbrightLoc, overbright ? 1 : 0);
// set textures
GL_SelectTexture (GL_TEXTURE0);
GL_Bind (tx);
if (fb)
{
GL_SelectTexture (GL_TEXTURE1);
GL_Bind (fb);
}
// draw
glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (void *)(intptr_t)currententity->model->vboindexofs);
// clean up
GL_DisableVertexAttribArrayFunc (texCoordsAttrIndex);
GL_DisableVertexAttribArrayFunc (pose1VertexAttrIndex);
GL_DisableVertexAttribArrayFunc (pose2VertexAttrIndex);
GL_DisableVertexAttribArrayFunc (pose1NormalAttrIndex);
GL_DisableVertexAttribArrayFunc (pose2NormalAttrIndex);
GL_UseProgramFunc (0);
GL_SelectTexture (GL_TEXTURE0);
rs_aliaspasses += paliashdr->numtris;
}
/*
=============
GL_DrawAliasFrame -- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat
@ -319,6 +569,8 @@ void R_SetupAliasLighting (entity_t *e)
vec3_t dist;
float add;
int i;
int quantizedangle;
float radiansangle;
R_LightPoint (e->origin);
@ -375,8 +627,19 @@ void R_SetupAliasLighting (entity_t *e)
lightcolor[2] = 256.0f;
}
shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)];
VectorScale(lightcolor, 1.0f / 200.0f, lightcolor);
quantizedangle = ((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1);
//ericw -- shadevector is passed to the shader to compute shadedots inside the
//shader, see GLAlias_CreateShaders()
radiansangle = (quantizedangle / 16.0) * 2.0 * 3.14159;
shadevector[0] = cos(-radiansangle);
shadevector[1] = sin(-radiansangle);
shadevector[2] = 1;
VectorNormalize(shadevector);
//ericw --
shadedots = r_avertexnormal_dots[quantizedangle];
VectorScale (lightcolor, 1.0f / 200.0f, lightcolor);
}
/*
@ -500,6 +763,12 @@ void R_DrawAliasModel (entity_t *e)
GL_DrawAliasFrame (paliashdr, lerpdata);
glEnable (GL_TEXTURE_2D);
}
// call fast path if possible. if the shader compliation failed for some reason,
// r_alias_program will be 0.
else if (GLAlias_SupportsShaders() && (r_alias_program != 0))
{
GL_DrawAliasFrame_GLSL (paliashdr, lerpdata, tx, fb);
}
else if (overbright)
{
if (gl_texture_env_combine && gl_mtexable && gl_texture_env_add && fb) //case 1: everything in one pass

View file

@ -959,7 +959,7 @@ void GL_BuildLightmaps (void)
=============================================================
*/
static GLuint gl_bmodel_vbo = 0;
GLuint gl_bmodel_vbo = 0;
/*
==================
@ -1021,23 +1021,9 @@ void GL_BuildVBOs (void)
GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo);
GL_BufferDataFunc (GL_ARRAY_BUFFER, varray_bytes, varray, GL_STATIC_DRAW);
free (varray);
// setup vertex array. this will need to move if we use vertex arrays for other things
glVertexPointer (3, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0));
glEnableClientState (GL_VERTEX_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE0_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 3);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE1_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 5);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
// TMU 2 is for fullbrights; same texture coordinates as TMU 0
GL_ClientActiveTextureFunc (GL_TEXTURE2_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 3);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
// invalidate the cached bindings
GL_ClearBufferBindings ();
}
/*

View file

@ -779,6 +779,8 @@ void R_DrawLightmapChains (void)
}
}
extern GLuint gl_bmodel_vbo;
/*
================
R_DrawTextureChains_Multitexture_VBO -- ericw
@ -796,6 +798,27 @@ void R_DrawTextureChains_Multitexture_VBO (qmodel_t *model, entity_t *ent, texch
int lastlightmap;
gltexture_t *fullbright = NULL;
// Bind the buffers
GL_BindBuffer (GL_ARRAY_BUFFER, gl_bmodel_vbo);
GL_BindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); // indices come from client memory!
// Setup vertex array pointers
glVertexPointer (3, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0));
glEnableClientState (GL_VERTEX_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE0_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 3);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE1_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 5);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
// TMU 2 is for fullbrights; same texture coordinates as TMU 0
GL_ClientActiveTextureFunc (GL_TEXTURE2_ARB);
glTexCoordPointer (2, GL_FLOAT, VERTEXSIZE * sizeof(float), ((float *)0) + 3);
glEnableClientState (GL_TEXTURE_COORD_ARRAY);
// Setup TMU 1 (lightmap)
GL_SelectTexture (GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
@ -804,7 +827,7 @@ void R_DrawTextureChains_Multitexture_VBO (qmodel_t *model, entity_t *ent, texch
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, gl_overbright.value ? 2.0f : 1.0f);
glEnable(GL_TEXTURE_2D);
// Setup TMU 2 (fullbrights)
GL_SelectTexture (GL_TEXTURE2_ARB);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
@ -873,6 +896,18 @@ void R_DrawTextureChains_Multitexture_VBO (qmodel_t *model, entity_t *ent, texch
GL_SelectTexture (GL_TEXTURE0_ARB);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
// Disable client state
glDisableClientState (GL_VERTEX_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE0_ARB);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE1_ARB);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
GL_ClientActiveTextureFunc (GL_TEXTURE2_ARB);
glDisableClientState (GL_TEXTURE_COORD_ARRAY);
}
/*