From fd3c4d26133c306c9f2fff3b8053d9b154893b83 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Wed, 22 Oct 2014 20:20:05 -0600 Subject: [PATCH] GLSL vertex shader for alias model lighting and lerping --- quakespasm/Quake/gl_mesh.c | 214 +++++++++++++++++++++++++++++++ quakespasm/Quake/gl_model.h | 36 ++++++ quakespasm/Quake/gl_rmisc.c | 1 + quakespasm/Quake/gl_vidsdl.c | 89 ++++++++++++- quakespasm/Quake/glquake.h | 51 ++++++++ quakespasm/Quake/r_alias.c | 235 ++++++++++++++++++++++++++++++++++- quakespasm/Quake/r_brush.c | 2 +- quakespasm/Quake/r_world.c | 22 ++++ 8 files changed, 645 insertions(+), 5 deletions(-) diff --git a/quakespasm/Quake/gl_mesh.c b/quakespasm/Quake/gl_mesh.c index fa13617d..e49e9b4c 100644 --- a/quakespasm/Quake/gl_mesh.c +++ b/quakespasm/Quake/gl_mesh.c @@ -287,6 +287,7 @@ void BuildTris (void) alltris += pheader->numtris; } +void GL_MakeAliasModelDisplayLists_VBO (void); /* ================ @@ -346,5 +347,218 @@ void GL_MakeAliasModelDisplayLists (qmodel_t *m, aliashdr_t *hdr) for (i=0 ; inumposes ; i++) for (j=0 ; jnumposes * paliashdr->numverts * sizeof(trivertx_t)); + paliashdr->vertexes = (byte *)verts - (byte *)paliashdr; + for (i=0 ; inumposes ; i++) + for (j=0 ; jnumverts ; 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 + +Original code by MH from RMQEngine +================ +*/ +void GLMesh_LoadVertexBuffers (void) +{ + int j; + qmodel_t *m; + int totalindexes = 0; + int totalvbosize = 0; + byte *vbodata; + + 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); + + vbodata = GL_MapBufferFunc (GL_ARRAY_BUFFER, GL_READ_WRITE); + + // 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 *) (vbodata + m->vboxyzofs + (f * 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 + } + } + + 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_UnmapBufferFunc (GL_ARRAY_BUFFER); + + GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, 0); + GL_BindBufferFunc (GL_ARRAY_BUFFER, 0); +} diff --git a/quakespasm/Quake/gl_model.h b/quakespasm/Quake/gl_model.h index db9f206a..7e8d09a8 100644 --- a/quakespasm/Quake/gl_model.h +++ b/quakespasm/Quake/gl_model.h @@ -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 // diff --git a/quakespasm/Quake/gl_rmisc.c b/quakespasm/Quake/gl_rmisc.c index 610fe6cc..89619914 100644 --- a/quakespasm/Quake/gl_rmisc.c +++ b/quakespasm/Quake/gl_rmisc.c @@ -274,6 +274,7 @@ void R_NewMap (void) GL_BuildLightmaps (); GL_BuildVBOs (); + GLMesh_LoadVertexBuffers (); r_framecount = 0; //johnfitz -- paranoid? r_visframecount = 0; //johnfitz -- paranoid? diff --git a/quakespasm/Quake/gl_vidsdl.c b/quakespasm/Quake/gl_vidsdl.c index 97d41b8a..e01f90a5 100644 --- a/quakespasm/Quake/gl_vidsdl.c +++ b/quakespasm/Quake/gl_vidsdl.c @@ -95,6 +95,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 @@ -102,9 +103,32 @@ 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 +PFNGLMAPBUFFERARBPROC GL_MapBufferFunc = NULL; //ericw +PFNGLUNMAPBUFFERARBPROC GL_UnmapBufferFunc = NULL; //ericw PFNGLDELETEBUFFERSARBPROC GL_DeleteBuffersFunc = NULL; //ericw PFNGLGENBUFFERSARBPROC GL_GenBuffersFunc = NULL; //ericw +QS_PFNGLCREATESHADERPROC GL_CreateShaderFunc = 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_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_PFNGLUNIFORM1FPROC GL_Uniform1fFunc = NULL; //ericw +QS_PFNGLUNIFORM3FPROC GL_Uniform3fFunc = NULL; //ericw +QS_PFNGLUNIFORM4FPROC GL_Uniform4fFunc = NULL; //ericw + //==================================== //johnfitz -- new cvars @@ -654,6 +678,7 @@ static void VID_Restart (void) GL_Init (); TexMgr_ReloadImages (); GL_BuildVBOs (); + GLMesh_LoadVertexBuffers (); GL_SetupState (); //warpimages needs to be recalculated @@ -819,9 +844,12 @@ 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_MapBufferFunc = (PFNGLMAPBUFFERARBPROC) SDL_GL_GetProcAddress("glMapBufferARB"); + GL_UnmapBufferFunc = (PFNGLUNMAPBUFFERARBPROC) SDL_GL_GetProcAddress("glUnmapBufferARB"); 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_MapBufferFunc && GL_UnmapBufferFunc && GL_DeleteBuffersFunc && GL_GenBuffersFunc) { Con_Printf("FOUND: ARB_vertex_buffer_object\n"); gl_vbo_able = true; @@ -989,6 +1017,63 @@ 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) + Con_Warning ("OpenGL version < 2, skipping GLSL check\n"); + else + { + GL_CreateShaderFunc = (QS_PFNGLCREATESHADERPROC) SDL_GL_GetProcAddress("glCreateShader"); + 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_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_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_ShaderSourceFunc && + GL_CompileShaderFunc && + GL_GetShaderivFunc && + GL_GetShaderInfoLogFunc && + GL_GetProgramivFunc && + GL_GetProgramInfoLogFunc && + GL_CreateProgramFunc && + GL_AttachShaderFunc && + GL_LinkProgramFunc && + GL_UseProgramFunc && + GL_GetAttribLocationFunc && + GL_VertexAttribPointerFunc && + GL_EnableVertexAttribArrayFunc && + GL_DisableVertexAttribArrayFunc && + GL_GetUniformLocationFunc && + GL_Uniform1fFunc && + GL_Uniform3fFunc && + GL_Uniform4fFunc) + { + Con_Printf("FOUND: GLSL\n"); + gl_glsl_able = true; + } + else + { + Con_Warning ("GLSL not available\n"); + } + } } /* @@ -1050,6 +1135,8 @@ static void GL_Init (void) Cbuf_AddText ("gl_clear 1"); } //johnfitz + + GLAlias_CreateShaders (); //ericw } /* diff --git a/quakespasm/Quake/glquake.h b/quakespasm/Quake/glquake.h index c7ea9e9e..4bad690e 100644 --- a/quakespasm/Quake/glquake.h +++ b/quakespasm/Quake/glquake.h @@ -167,11 +167,59 @@ extern qboolean gl_anisotropy_able; //ericw -- VBO extern PFNGLBINDBUFFERARBPROC GL_BindBufferFunc; extern PFNGLBUFFERDATAARBPROC GL_BufferDataFunc; +extern PFNGLBUFFERSUBDATAARBPROC GL_BufferSubDataFunc; +extern PFNGLMAPBUFFERARBPROC GL_MapBufferFunc; +extern PFNGLUNMAPBUFFERARBPROC GL_UnmapBufferFunc; 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_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_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_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_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_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_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; @@ -277,6 +325,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); @@ -291,6 +340,8 @@ void R_DrawBrushModel_ShowTris (entity_t *e); void R_DrawAliasModel_ShowTris (entity_t *e); void R_DrawParticles_ShowTris (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); diff --git a/quakespasm/Quake/r_alias.c b/quakespasm/Quake/r_alias.c index b1999182..68daaac9 100644 --- a/quakespasm/Quake/r_alias.c +++ b/quakespasm/Quake/r_alias.c @@ -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,215 @@ typedef struct { } lerpdata_t; //johnfitz +extern GLuint r_meshvbo; +extern GLuint r_meshindexesvbo; + +static GLuint shader; +static GLuint program; +static GLuint blendLoc; +static GLuint shadevectorLoc; +static GLuint lightColorLoc; + +static GLint pose1VertexAttrIndex; +static GLint pose1NormalAttrIndex; +static GLint pose2VertexAttrIndex; +static GLint pose2NormalAttrIndex; + +qboolean GLAlias_SupportsShaders (void) +{ + return gl_glsl_able && gl_vbo_able && gl_max_texture_units >= 3; +} + +void GLAlias_CreateShaders (void) +{ + GLint status; + const GLchar *source = \ + "#version 110\n" + "\n" + "uniform float Blend;\n" + "uniform vec3 ShadeVector;\n" + "uniform vec4 LightColor;\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] = gl_MultiTexCoord0;\n" + " gl_TexCoord[1] = gl_MultiTexCoord0;\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"; + + if (!GLAlias_SupportsShaders()) + return; + + shader = GL_CreateShaderFunc(GL_VERTEX_SHADER); + GL_ShaderSourceFunc(shader, 1, &source, NULL); + GL_CompileShaderFunc(shader); + + GL_GetShaderivFunc(shader, GL_COMPILE_STATUS, &status); + + if (status != GL_TRUE) + { + static char infolog[65536]; + GL_GetShaderInfoLogFunc (shader, 65536, NULL, infolog); + + printf("Shader info log: %s\n", infolog); + Sys_Error("Shader failed to compile"); + } + + // create program + program = GL_CreateProgramFunc(); + GL_AttachShaderFunc(program, shader); + GL_LinkProgramFunc(program); + + GL_GetProgramivFunc(program, GL_LINK_STATUS, &status); + + if (status != GL_TRUE) + { + Sys_Error("Program failed to link"); + } + + // get uniform location + + blendLoc = GL_GetUniformLocationFunc(program, "Blend"); + if (blendLoc == -1) + { + Sys_Error("GL_GetUniformLocationFunc Blend failed"); + } + + shadevectorLoc = GL_GetUniformLocationFunc(program, "ShadeVector"); + if (shadevectorLoc == -1) + { + Sys_Error("GL_GetUniformLocationFunc shadevector failed"); + } + + lightColorLoc = GL_GetUniformLocationFunc(program, "LightColor"); + if (lightColorLoc == -1) + { + Sys_Error("GL_GetUniformLocationFunc LightColor failed"); + } + + // get attributes + + pose1VertexAttrIndex = GL_GetAttribLocationFunc(program, "Pose1Vert"); + pose1NormalAttrIndex = GL_GetAttribLocationFunc(program, "Pose1Normal"); + + pose2VertexAttrIndex = GL_GetAttribLocationFunc(program, "Pose2Vert"); + pose2NormalAttrIndex = GL_GetAttribLocationFunc(program, "Pose2Normal"); +} + +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); +} + +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); +} + +/* +============= +GL_DrawAliasFrame_GLSL -- ericw + +Based on code by MH from RMQEngine +============= +*/ +void GL_DrawAliasFrame_GLSL (aliashdr_t *paliashdr, lerpdata_t lerpdata) +{ + 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(program); + + GL_BindBufferFunc (GL_ARRAY_BUFFER_ARB, r_meshvbo); + GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER_ARB, r_meshindexesvbo); + + GL_VertexAttribPointerFunc (pose1VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose1)); + GL_EnableVertexAttribArrayFunc (pose1VertexAttrIndex); + + GL_VertexAttribPointerFunc (pose2VertexAttrIndex, 4, GL_UNSIGNED_BYTE, GL_FALSE, sizeof (meshxyz_t), GLARB_GetXYZOffset (paliashdr, lerpdata.pose2)); + GL_EnableVertexAttribArrayFunc (pose2VertexAttrIndex); + + GL_ClientActiveTextureFunc (GL_TEXTURE0_ARB); + glTexCoordPointer (2, GL_FLOAT, 0, (void *)(intptr_t)currententity->model->vbostofs); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + + GL_ClientActiveTextureFunc (GL_TEXTURE1_ARB); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + + GL_ClientActiveTextureFunc (GL_TEXTURE2_ARB); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); + + +// GL_TRUE to normalize the signed bytes to [-1 .. 1] + GL_VertexAttribPointerFunc (pose1NormalAttrIndex, 3, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose1)); + GL_EnableVertexAttribArrayFunc (pose1NormalAttrIndex); + + GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 3, GL_BYTE, GL_TRUE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose2)); + GL_EnableVertexAttribArrayFunc (pose2NormalAttrIndex); + + // 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); + + // draw + + glDrawElements (GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (void *)(intptr_t)currententity->model->vboindexofs); + + // clean up + + GL_DisableVertexAttribArrayFunc (pose1VertexAttrIndex); + GL_DisableVertexAttribArrayFunc (pose2VertexAttrIndex); + + 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); + + GL_DisableVertexAttribArrayFunc (pose1NormalAttrIndex); + GL_DisableVertexAttribArrayFunc (pose2NormalAttrIndex); + + GL_UseProgramFunc (0); + + rs_aliaspasses += paliashdr->numtris; +} + /* ============= GL_DrawAliasFrame -- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat @@ -82,6 +291,13 @@ void GL_DrawAliasFrame (aliashdr_t *paliashdr, lerpdata_t lerpdata) float blend, iblend; qboolean lerping; + // call fast path if possible + if (GLAlias_SupportsShaders() && !r_drawflat_cheatsafe && shading) + { + GL_DrawAliasFrame_GLSL (paliashdr, lerpdata); + return; + } + if (lerpdata.pose1 != lerpdata.pose2) { lerping = true; @@ -319,7 +535,9 @@ void R_SetupAliasLighting (entity_t *e) vec3_t dist; float add; int i; - + int quantizedangle; + float radiansangle; + R_LightPoint (e->origin); //add dlights @@ -374,8 +592,19 @@ void R_SetupAliasLighting (entity_t *e) lightcolor[1] = 256.0f; lightcolor[2] = 256.0f; } + + quantizedangle = ((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1); - shadedots = r_avertexnormal_dots[((int)(e->angles[1] * (SHADEDOT_QUANT / 360.0))) & (SHADEDOT_QUANT - 1)]; + // ericw -- passed to shader, used to reproduce shadedots table lookup + 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); } diff --git a/quakespasm/Quake/r_brush.c b/quakespasm/Quake/r_brush.c index b5203ebc..7de6df28 100644 --- a/quakespasm/Quake/r_brush.c +++ b/quakespasm/Quake/r_brush.c @@ -959,7 +959,7 @@ void GL_BuildLightmaps (void) ============================================================= */ -static GLuint gl_bmodel_vbo = 0; +GLuint gl_bmodel_vbo = 0; /* ================== diff --git a/quakespasm/Quake/r_world.c b/quakespasm/Quake/r_world.c index 1f9c41be..159eaa63 100644 --- a/quakespasm/Quake/r_world.c +++ b/quakespasm/Quake/r_world.c @@ -779,6 +779,8 @@ void R_DrawLightmapChains (void) } } +extern GLuint gl_bmodel_vbo; + /* ================ R_DrawTextureChains_Multitexture_VBO -- ericw @@ -796,6 +798,26 @@ void R_DrawTextureChains_Multitexture_VBO (qmodel_t *model, entity_t *ent, texch int lastlightmap; gltexture_t *fullbright = NULL; + GL_BindBufferFunc (GL_ARRAY_BUFFER_ARB, gl_bmodel_vbo); + GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER_ARB, 0); // so we pull them from client memory! + +// 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); + // Setup TMU 1 (lightmap) GL_SelectTexture (GL_TEXTURE1_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);