diff --git a/quakespasm/Quake/gl_mesh.c b/quakespasm/Quake/gl_mesh.c index fa13617d..84ba96bb 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]; + // ericw -- + + // there can never be more than this number of verts and we just put them all on the hunk + maxverts_vbo = pheader->numtris * 3; + aliasmesh_t *desc = (aliasmesh_t *) Hunk_Alloc (sizeof (aliasmesh_t) * maxverts_vbo); + aliasmesh_t *mesh = (aliasmesh_t *) malloc (sizeof (aliasmesh_t) * maxverts_vbo); + + // there will always be this number of indexes + unsigned short *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 (mesh[v].vertindex == vertindex && (int) mesh[v].st[0] == s && (int) mesh[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; + + mesh[pheader->numverts_vbo].vertindex = vertindex; + mesh[pheader->numverts_vbo].st[0] = s; + mesh[pheader->numverts_vbo++].st[1] = t; + } + } + } + + memcpy(desc, mesh, sizeof (aliasmesh_t) * pheader->numverts_vbo); + + // free our temp holding area for mesh verts + free (mesh); + + // create a hunk buffer for the final mesh we'll actually use + { + // vertex/fragment programs interpolate on the GPU and need to access the data directly + unsigned short *hunkndx = (unsigned short *) Hunk_Alloc (sizeof (unsigned short) * pheader->numverts_vbo); + + // move these to aliasmesh struct??? + pheader->vertindexes = (intptr_t) hunkndx - (intptr_t) pheader; + + for (i = 0; i < pheader->numverts_vbo; i++) + { + hunkndx[i] = desc[i].vertindex; + } + } +} + +static char scratchbuf[65536]; + +#define NUMVERTEXNORMALS 162 +extern float r_avertexnormals[NUMVERTEXNORMALS][3]; + +GLuint r_meshvbo = 0; +GLuint r_meshindexesvbo = 0; + +void GLMesh_LoadVertexBuffers (void) +{ + int j; + qmodel_t *m; + int totalindexes = 0; + int totalvbosize = 0; + + // 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); + + hdr->vboindexofs = (totalindexes * sizeof (unsigned short)); + totalindexes += hdr->numindexes; + + hdr->vboxyzofs = totalvbosize; + totalvbosize += (hdr->numposes * hdr->numverts_vbo * sizeof (meshxyz_t)); // ericw -- what RMQEngine called nummeshframes is called numposes in QuakeSpasm + + hdr->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, + hdr->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 *) scratchbuf; // FIXME - potentially unsafe here + 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 + + xyz[v].normal[0] = r_avertexnormals[trivert.lightnormalindex][0]; + xyz[v].normal[1] = r_avertexnormals[trivert.lightnormalindex][1]; + xyz[v].normal[2] = r_avertexnormals[trivert.lightnormalindex][2]; + } + + GL_BufferSubDataFunc (GL_ARRAY_BUFFER, + hdr->vboxyzofs + (f * hdr->numverts_vbo * sizeof (meshxyz_t)), + hdr->numverts_vbo * sizeof (meshxyz_t), + xyz); + } + + st = (meshst_t *) scratchbuf; + + 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, + hdr->vbostofs, + hdr->numverts_vbo * sizeof (meshst_t), + st); + } + + GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, 0); + GL_BindBufferFunc (GL_ARRAY_BUFFER, 0); +} diff --git a/quakespasm/Quake/gl_model.c b/quakespasm/Quake/gl_model.c index 1cb58449..4bf29171 100644 --- a/quakespasm/Quake/gl_model.c +++ b/quakespasm/Quake/gl_model.c @@ -2515,7 +2515,7 @@ void Mod_LoadAliasModel (qmodel_t *mod, void *buffer) Sys_Error ("model %s has too many vertices", mod->name); pheader->numtris = LittleLong (pinmodel->numtris); - + if (pheader->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); diff --git a/quakespasm/Quake/gl_model.h b/quakespasm/Quake/gl_model.h index db9f206a..8bc1d879 100644 --- a/quakespasm/Quake/gl_model.h +++ b/quakespasm/Quake/gl_model.h @@ -282,6 +282,25 @@ Alias models are position independent, so the cache manager can move them. ============================================================================== */ +// 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]; + float normal[3]; +} meshxyz_t; + + +typedef struct meshst_s +{ + float st[2]; +} meshst_t; + typedef struct { int firstpose; @@ -326,11 +345,27 @@ typedef struct { int skinwidth; int skinheight; int numverts; + int numverts_vbo; int numtris; int numframes; synctype_t synctype; int flags; float size; + + intptr_t meshdesc; + intptr_t indexes; + int numindexes; + + // only shaders use this + intptr_t vertindexes; + + // only vbos use these + int vboindexofs; + int vboxyzofs; + int vbostofs; + + // offset to (trivertx_t *) Hunk_Alloc (paliashdr->numposes * paliashdr->vertsperframe * sizeof(trivertx_t)) + intptr_t vertexes; int numposes; int poseverts; diff --git a/quakespasm/Quake/gl_rmain.c b/quakespasm/Quake/gl_rmain.c index f97cdd06..cf68b063 100644 --- a/quakespasm/Quake/gl_rmain.c +++ b/quakespasm/Quake/gl_rmain.c @@ -456,6 +456,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 @@ -463,15 +468,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 ; ivboxyzofs + (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 *) (hdr->vboxyzofs + (hdr->numverts_vbo * pose * sizeof (meshxyz_t)) + normaloffs); +} + /* ============= GL_DrawAliasFrame -- johnfitz -- rewritten to support colored light, lerping, entalpha, multitexture, and r_drawflat @@ -105,6 +122,165 @@ void GL_DrawAliasFrame (aliashdr_t *paliashdr, lerpdata_t lerpdata) vertcolor[3] = entalpha; //never changes, so there's no need to put this inside the loop +// ericw -- shader + + 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; + + if (shader == 0) + { + const GLchar *source = \ + "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)\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" + " gl_Position = gl_ModelViewProjectionMatrix * mix(Pose1Vert, Pose2Vert, Blend);\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" + "}\n" + ; + shader = GL_CreateShaderFunc(GL_VERTEX_SHADER); + GL_ShaderSourceFunc(shader, 1, &source, NULL); + GL_CompileShaderFunc(shader); + + GLint status; + 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"); + } + + GL_UseProgramFunc(program); + +// ericw -- + + + +// ericw -- bind it and stuff + GL_BindBufferFunc (GL_ARRAY_BUFFER, r_meshvbo); + GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, 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 *) paliashdr->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_VertexAttribPointerFunc (pose1NormalAttrIndex, 3, GL_FLOAT, GL_FALSE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose1)); + GL_EnableVertexAttribArrayFunc (pose1NormalAttrIndex); + + GL_VertexAttribPointerFunc (pose2NormalAttrIndex, 3, GL_FLOAT, GL_FALSE, sizeof (meshxyz_t), GLARB_GetNormalOffset (paliashdr, lerpdata.pose2)); + GL_EnableVertexAttribArrayFunc (pose2NormalAttrIndex); + + // Uniform + + GL_Uniform1fFunc(blendLoc, blend); + GL_Uniform3fFunc(shadevectorLoc, shadevector[0], shadevector[1], shadevector[2]); + GL_Uniform4fFunc(lightColorLoc, lightcolor[0], lightcolor[1], lightcolor[2], entalpha); + +// +// ericw -- bind it and stuff + +// GL_SelectTexture (GL_TEXTURE0_ARB); +// glEnable(GL_TEXTURE_2D); + glDrawElements(GL_TRIANGLES, paliashdr->numindexes, GL_UNSIGNED_SHORT, (void *)paliashdr->vboindexofs); + + + 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); + glDisableClientState (GL_NORMAL_ARRAY); + + GL_UseProgramFunc(0); + + return; + + while (1) { // get the vertex count and primitive type @@ -374,8 +550,17 @@ void R_SetupAliasLighting (entity_t *e) lightcolor[1] = 256.0f; lightcolor[2] = 256.0f; } + + int quantangle = ((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)]; + float radAngle = (quantangle / 16.0) * 2.0 * 3.14159; + shadevector[0] = cos(-radAngle); + shadevector[1] = sin(-radAngle); + shadevector[2] = 1; + VectorNormalize(shadevector); + + shadedots = r_avertexnormal_dots[quantangle]; + 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 73e095d8..9caa6e44 100644 --- a/quakespasm/Quake/r_world.c +++ b/quakespasm/Quake/r_world.c @@ -770,6 +770,8 @@ void R_DrawLightmapChains (void) } } +extern GLuint gl_bmodel_vbo; + /* ================ R_DrawTextureChains_Multitexture_VBO -- ericw @@ -787,6 +789,29 @@ void R_DrawTextureChains_Multitexture_VBO (qmodel_t *model, entity_t *ent, texch int lastlightmap; gltexture_t *fullbright = NULL; +// ericw -- bind it and stuff + GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo); + GL_BindBufferFunc (GL_ELEMENT_ARRAY_BUFFER, 0); // ericw -- 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); +// ericw -- bind it and stuff + + // Setup TMU 1 (lightmap) GL_SelectTexture (GL_TEXTURE1_ARB); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);