/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2007-2008 Kristian Duske Copyright (C) 2010-2014 QuakeSpasm developers This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // r_brush.c: brush model rendering. renamed from r_surf.c #include "quakedef.h" extern cvar_t gl_fullbrights, r_drawflat, gl_overbright, r_oldwater; //johnfitz extern cvar_t gl_zfix; // QuakeSpasm z-fighting fix int gl_lightmap_format; int lightmap_bytes; qboolean lightmaps_latecached; #define MAX_SANITY_LIGHTMAPS (1u<<20) struct lightmap_s *lightmaps; int lightmap_count; int last_lightmap_allocated; int allocated[LMBLOCK_WIDTH]; unsigned blocklights[LMBLOCK_WIDTH*LMBLOCK_HEIGHT*3]; //johnfitz -- was 18*18, added lit support (*3) and loosened surface extents maximum (LMBLOCK_WIDTH*LMBLOCK_HEIGHT) /* =============== R_TextureAnimation -- johnfitz -- added "frame" param to eliminate use of "currententity" global Returns the proper texture for a given time and base texture =============== */ texture_t *R_TextureAnimation (texture_t *base, int frame) { int relative; int count; if (frame) if (base->alternate_anims) base = base->alternate_anims; if (!base->anim_total) return base; relative = (int)(cl.time*10) % base->anim_total; count = 0; while (base->anim_min > relative || base->anim_max <= relative) { base = base->anim_next; if (!base) Sys_Error ("R_TextureAnimation: broken cycle"); if (++count > 100) Sys_Error ("R_TextureAnimation: infinite cycle"); } return base; } /* ================ DrawGLPoly ================ */ void DrawGLPoly (glpoly_t *p) { float *v; int i; glBegin (GL_POLYGON); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[3], v[4]); glVertex3fv (v); } glEnd (); } /* ================ DrawGLTriangleFan -- johnfitz -- like DrawGLPoly but for r_showtris ================ */ void DrawGLTriangleFan (glpoly_t *p) { float *v; int i; glBegin (GL_TRIANGLE_FAN); v = p->verts[0]; for (i=0 ; inumverts ; i++, v+= VERTEXSIZE) { glVertex3fv (v); } glEnd (); } /* ============================================================= BRUSH MODELS ============================================================= */ #if 0 /* ================ R_DrawSequentialPoly -- johnfitz -- rewritten ================ */ void R_DrawSequentialPoly (msurface_t *s) { glpoly_t *p; texture_t *t; float *v; float entalpha; int i; t = R_TextureAnimation (s->texinfo->texture, currententity->frame); entalpha = ENTALPHA_DECODE(currententity->alpha); // drawflat if (r_drawflat_cheatsafe) { if ((s->flags & SURF_DRAWTURB) && r_oldwater.value) { for (p = s->polys->next; p; p = p->next) { srand((unsigned int) (uintptr_t) p); glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0); DrawGLPoly (p); rs_brushpasses++; } return; } srand((unsigned int) (uintptr_t) s->polys); glColor3f (rand()%256/255.0, rand()%256/255.0, rand()%256/255.0); DrawGLPoly (s->polys); rs_brushpasses++; return; } // fullbright if ((r_fullbright_cheatsafe) && !(s->flags & SURF_DRAWTILED)) { if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } if (s->flags & SURF_DRAWFENCE) glEnable (GL_ALPHA_TEST); // Flip on alpha test GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; if (s->flags & SURF_DRAWFENCE) glDisable (GL_ALPHA_TEST); // Flip alpha test back off if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } goto fullbrights; } // r_lightmap if (r_lightmap_cheatsafe) { if (s->flags & SURF_DRAWTILED) { glDisable (GL_TEXTURE_2D); DrawGLPoly (s->polys); glEnable (GL_TEXTURE_2D); rs_brushpasses++; return; } R_RenderDynamicLightmaps (s); GL_Bind (lightmap_textures[s->lightmaptexturenum]); if (!gl_overbright.value) { glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0.5, 0.5, 0.5); } glBegin (GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; ipolys->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[5], v[6]); glVertex3fv (v); } glEnd (); if (!gl_overbright.value) { glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } rs_brushpasses++; return; } // sky poly -- skip it, already handled in gl_sky.c if (s->flags & SURF_DRAWSKY) return; // water poly if (s->flags & SURF_DRAWTURB) { if (currententity->alpha == ENTALPHA_DEFAULT) entalpha = CLAMP(0.0, GL_WaterAlphaForSurface(s), 1.0); if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } if (r_oldwater.value) { GL_Bind (s->texinfo->texture->gltexture); for (p = s->polys->next; p; p = p->next) { DrawWaterPoly (p); rs_brushpasses++; } rs_brushpasses++; } else { GL_Bind (s->texinfo->texture->warpimage); s->texinfo->texture->update_warp = true; // FIXME: one frame too late! DrawGLPoly (s->polys); rs_brushpasses++; } if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } return; } // missing texture if (s->flags & SURF_NOTEXTURE) { if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } return; } // lightmapped poly if (entalpha < 1) { glDepthMask(GL_FALSE); glEnable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor4f(1, 1, 1, entalpha); } else glColor3f(1, 1, 1); if (s->flags & SURF_DRAWFENCE) glEnable (GL_ALPHA_TEST); // Flip on alpha test if (gl_overbright.value) { if (gl_texture_env_combine && gl_mtexable) //case 1: texture and lightmap in one pass, overbright using texture combiners { GL_DisableMultitexture(); // selects TEXTURE0 GL_Bind (t->gltexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_EnableMultitexture(); // selects TEXTURE1 GL_Bind (lightmap_textures[s->lightmaptexturenum]); R_RenderDynamicLightmaps (s); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PREVIOUS_EXT); glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_TEXTURE); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 2.0f); glBegin(GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; ipolys->numverts ; i++, v+= VERTEXSIZE) { GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]); GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv (v); } glEnd (); glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, 1.0f); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_DisableMultitexture (); rs_brushpasses++; } else if (entalpha < 1 || (s->flags & SURF_DRAWFENCE)) //case 2: can't do multipass if entity has alpha, so just draw the texture { GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; } else //case 3: texture in one pass, lightmap in second pass using 2x modulation blend func, fog in third pass { //first pass -- texture with no fog Fog_DisableGFog (); GL_Bind (t->gltexture); DrawGLPoly (s->polys); Fog_EnableGFog (); rs_brushpasses++; //second pass -- lightmap with black fog, modulate blended R_RenderDynamicLightmaps (s); GL_Bind (lightmap_textures[s->lightmaptexturenum]); glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR); //2x modulate Fog_StartAdditive (); glBegin (GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; ipolys->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[5], v[6]); glVertex3fv (v); } glEnd (); Fog_StopAdditive (); rs_brushpasses++; //third pass -- black geo with normal fog, additive blended if (Fog_GetDensity() > 0) { glBlendFunc(GL_ONE, GL_ONE); //add glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0,0,0); DrawGLPoly (s->polys); glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); rs_brushpasses++; } glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); } } else { if (gl_mtexable) //case 4: texture and lightmap in one pass, regular modulation { GL_DisableMultitexture(); // selects TEXTURE0 GL_Bind (t->gltexture); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); GL_EnableMultitexture(); // selects TEXTURE1 GL_Bind (lightmap_textures[s->lightmaptexturenum]); R_RenderDynamicLightmaps (s); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBegin(GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; ipolys->numverts ; i++, v+= VERTEXSIZE) { GL_MTexCoord2fFunc (GL_TEXTURE0_ARB, v[3], v[4]); GL_MTexCoord2fFunc (GL_TEXTURE1_ARB, v[5], v[6]); glVertex3fv (v); } glEnd (); GL_DisableMultitexture (); rs_brushpasses++; } else if (entalpha < 1 || (s->flags & SURF_DRAWFENCE)) //case 5: can't do multipass if entity has alpha, so just draw the texture { GL_Bind (t->gltexture); DrawGLPoly (s->polys); rs_brushpasses++; } else //case 6: texture in one pass, lightmap in a second pass, fog in third pass { //first pass -- texture with no fog Fog_DisableGFog (); GL_Bind (t->gltexture); DrawGLPoly (s->polys); Fog_EnableGFog (); rs_brushpasses++; //second pass -- lightmap with black fog, modulate blended R_RenderDynamicLightmaps (s); GL_Bind (lightmap_textures[s->lightmaptexturenum]); glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc (GL_ZERO, GL_SRC_COLOR); //modulate Fog_StartAdditive (); glBegin (GL_POLYGON); v = s->polys->verts[0]; for (i=0 ; ipolys->numverts ; i++, v+= VERTEXSIZE) { glTexCoord2f (v[5], v[6]); glVertex3fv (v); } glEnd (); Fog_StopAdditive (); rs_brushpasses++; //third pass -- black geo with normal fog, additive blended if (Fog_GetDensity() > 0) { glBlendFunc(GL_ONE, GL_ONE); //add glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f(0,0,0); DrawGLPoly (s->polys); glColor3f(1,1,1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); rs_brushpasses++; } glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); } } if (entalpha < 1) { glDepthMask(GL_TRUE); glDisable(GL_BLEND); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glColor3f(1, 1, 1); } if (s->flags & SURF_DRAWFENCE) glDisable (GL_ALPHA_TEST); // Flip alpha test back off fullbrights: if (gl_fullbrights.value && t->fullbright) { glDepthMask (GL_FALSE); glEnable (GL_BLEND); glBlendFunc (GL_ONE, GL_ONE); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glColor3f (entalpha, entalpha, entalpha); GL_Bind (t->fullbright); Fog_StartAdditive (); DrawGLPoly (s->polys); Fog_StopAdditive (); glColor3f(1, 1, 1); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable (GL_BLEND); glDepthMask (GL_TRUE); rs_brushpasses++; } } #endif /* ================= R_DrawBrushModel ================= */ void R_DrawBrushModel (entity_t *e) { int i, k; msurface_t *psurf; float dot; mplane_t *pplane; qmodel_t *clmodel; vec3_t lightorg; if (R_CullModelForEntity(e)) return; currententity = e; clmodel = e->model; VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (e->angles[0] || e->angles[1] || e->angles[2]) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; // calculate dynamic lighting for bmodel if it's not an // instanced model if (clmodel->firstmodelsurface != 0 && !gl_flashblend.value) { for (k=0 ; korigin, lightorg); R_MarkLights (&cl_dlights[k], lightorg, k, clmodel->nodes + clmodel->hulls[0].firstclipnode); } } glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug if (gl_zfix.value) { e->origin[0] -= DIST_EPSILON; e->origin[1] -= DIST_EPSILON; e->origin[2] -= DIST_EPSILON; } R_RotateForEntity (e->origin, e->angles, e->netstate.scale); if (gl_zfix.value) { e->origin[0] += DIST_EPSILON; e->origin[1] += DIST_EPSILON; e->origin[2] += DIST_EPSILON; } e->angles[0] = -e->angles[0]; // stupid quake bug R_ClearTextureChains (clmodel, chain_model); for (i=0 ; inummodelsurfaces ; i++, psurf++) { pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { R_ChainSurface (psurf, chain_model); R_RenderDynamicLightmaps(clmodel, psurf); rs_brushpolys++; } } R_DrawTextureChains (clmodel, e, chain_model); R_DrawTextureChains_Water (clmodel, e, chain_model); glPopMatrix (); } /* ================= R_DrawBrushModel_ShowTris -- johnfitz ================= */ void R_DrawBrushModel_ShowTris (entity_t *e) { int i; msurface_t *psurf; float dot; mplane_t *pplane; qmodel_t *clmodel; glpoly_t *p; if (R_CullModelForEntity(e)) return; currententity = e; clmodel = e->model; VectorSubtract (r_refdef.vieworg, e->origin, modelorg); if (e->angles[0] || e->angles[1] || e->angles[2]) { vec3_t temp; vec3_t forward, right, up; VectorCopy (modelorg, temp); AngleVectors (e->angles, forward, right, up); modelorg[0] = DotProduct (temp, forward); modelorg[1] = -DotProduct (temp, right); modelorg[2] = DotProduct (temp, up); } psurf = &clmodel->surfaces[clmodel->firstmodelsurface]; glPushMatrix (); e->angles[0] = -e->angles[0]; // stupid quake bug R_RotateForEntity (e->origin, e->angles, e->netstate.scale); e->angles[0] = -e->angles[0]; // stupid quake bug // // draw it // for (i=0 ; inummodelsurfaces ; i++, psurf++) { pplane = psurf->plane; dot = DotProduct (modelorg, pplane->normal) - pplane->dist; if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) { if ((psurf->flags & SURF_DRAWTURB) && r_oldwater.value) for (p = psurf->polys->next; p; p = p->next) DrawGLTriangleFan (p); else DrawGLTriangleFan (psurf->polys); } } glPopMatrix (); } /* ============================================================= LIGHTMAPS ============================================================= */ /* ================ R_RenderDynamicLightmaps called during rendering ================ */ void R_RenderDynamicLightmaps (qmodel_t *model, msurface_t *fa) { byte *base; int maps; glRect_t *theRect; int smax, tmax; if (fa->flags & SURF_DRAWTILED) //johnfitz -- not a lightmapped surface return; // add to lightmap chain fa->polys->chain = lightmaps[fa->lightmaptexturenum].polys; lightmaps[fa->lightmaptexturenum].polys = fa->polys; // check for lightmap modification for (maps=0; maps < MAXLIGHTMAPS && fa->styles[maps] != INVALID_LIGHTSTYLE; maps++) if (d_lightstylevalue[fa->styles[maps]] != fa->cached_light[maps]) goto dynamic; if (fa->dlightframe == r_framecount // dynamic this frame || fa->cached_dlight) // dynamic previously { dynamic: if (r_dynamic.value) { struct lightmap_s *lm = &lightmaps[fa->lightmaptexturenum]; lm->modified = true; theRect = &lm->rectchange; if (fa->light_t < theRect->t) { if (theRect->h) theRect->h += theRect->t - fa->light_t; theRect->t = fa->light_t; } if (fa->light_s < theRect->l) { if (theRect->w) theRect->w += theRect->l - fa->light_s; theRect->l = fa->light_s; } smax = (fa->extents[0]>>fa->lmshift)+1; tmax = (fa->extents[1]>>fa->lmshift)+1; if ((theRect->w + theRect->l) < (fa->light_s + smax)) theRect->w = (fa->light_s-theRect->l)+smax; if ((theRect->h + theRect->t) < (fa->light_t + tmax)) theRect->h = (fa->light_t-theRect->t)+tmax; base = lm->data; base += fa->light_t * LMBLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (model, fa, base, LMBLOCK_WIDTH*lightmap_bytes); } } } /* ======================== AllocBlock -- returns a texture number and the position inside it ======================== */ int AllocBlock (int w, int h, int *x, int *y) { int i, j; int best, best2; int texnum; // ericw -- rather than searching starting at lightmap 0 every time, // start at the last lightmap we allocated a surface in. // This makes AllocBlock much faster on large levels (can shave off 3+ seconds // of load time on a level with 180 lightmaps), at a cost of not quite packing // lightmaps as tightly vs. not doing this (uses ~5% more lightmaps) for (texnum=last_lightmap_allocated ; texnum= best) break; if (allocated[i+j] > best2) best2 = allocated[i+j]; } if (j == w) { // this is a valid spot *x = i; *y = best = best2; } } if (best + h > LMBLOCK_HEIGHT) continue; for (i=0 ; iextents[0]>>surf->lmshift)+1; tmax = (surf->extents[1]>>surf->lmshift)+1; surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); base = lightmaps[surf->lightmaptexturenum].data; base += (surf->light_t * LMBLOCK_WIDTH + surf->light_s) * lightmap_bytes; R_BuildLightMap (model, surf, base, LMBLOCK_WIDTH*lightmap_bytes); } /* ================ BuildSurfaceDisplayList -- called at level load time ================ */ void BuildSurfaceDisplayList (msurface_t *fa) { int i, lindex, lnumverts; medge_t *pedges, *r_pedge; float *vec; float s, t; glpoly_t *poly; int lmscale = (1<lmshift); // reconstruct the polygon pedges = currentmodel->edges; lnumverts = fa->numedges; // // draw texture // poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); poly->next = fa->polys; fa->polys = poly; poly->numverts = lnumverts; for (i=0 ; isurfedges[fa->firstedge + i]; if (lindex > 0) { r_pedge = &pedges[lindex]; vec = r_pcurrentvertbase[r_pedge->v[0]].position; } else { r_pedge = &pedges[-lindex]; vec = r_pcurrentvertbase[r_pedge->v[1]].position; } s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s /= fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t /= fa->texinfo->texture->height; VectorCopy (vec, poly->verts[i]); poly->verts[i][3] = s; poly->verts[i][4] = t; // Q64 RERELEASE texture shift if (fa->texinfo->texture->shift > 0) { poly->verts[i][3] /= ( 2 * fa->texinfo->texture->shift); poly->verts[i][4] /= ( 2 * fa->texinfo->texture->shift); } // // lightmap texture coordinates // s = DotProduct (vec, fa->texinfo->vecs[0]) + fa->texinfo->vecs[0][3]; s -= fa->texturemins[0]; s += fa->light_s*lmscale; s += lmscale/2.0; s /= LMBLOCK_WIDTH*lmscale; //fa->texinfo->texture->width; t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; t -= fa->texturemins[1]; t += fa->light_t*lmscale; t += lmscale/2.0; t /= LMBLOCK_HEIGHT*lmscale; //fa->texinfo->texture->height; poly->verts[i][5] = s; poly->verts[i][6] = t; } //johnfitz -- removed gl_keeptjunctions code poly->numverts = lnumverts; } /* Makes sure the model is good to go. */ void GL_BuildModel (qmodel_t *m) { int i; if (m->name[0] == '*') return; r_pcurrentvertbase = m->vertexes; currentmodel = m; for (i=0 ; inumsurfaces ; i++) { //johnfitz -- rewritten to use SURF_DRAWTILED instead of the sky/water flags if (m->surfaces[i].flags & SURF_DRAWTILED) continue; GL_CreateSurfaceLightmap (m, m->surfaces + i); BuildSurfaceDisplayList (m->surfaces + i); //johnfitz } // GL_BuildBModelVertexBuffer(); } /* ================== GL_BuildLightmaps -- called at level load time Builds the lightmap texture with all the surfaces from all brush models ================== */ void GL_BuildLightmaps (void) { char name[24]; int i, j; struct lightmap_s *lm; qmodel_t *m; r_framecount = 1; // no dlightcache //Spike -- wipe out all the lightmap data (johnfitz -- the gltexture objects were already freed by Mod_ClearAll) for (i=0; i < lightmap_count; i++) { if (lightmaps[i].texture) TexMgr_FreeTexture(lightmaps[i].texture); free(lightmaps[i].data); } free(lightmaps); lightmaps = NULL; last_lightmap_allocated = 0; lightmap_count = 0; gl_lightmap_format = GL_RGBA;//FIXME: hardcoded for now! switch (gl_lightmap_format) { case GL_RGBA: lightmap_bytes = 4; break; case GL_BGRA: lightmap_bytes = 4; break; default: Sys_Error ("GL_BuildLightmaps: bad lightmap format"); } for (j=1 ; jmodified = false; lm->rectchange.l = LMBLOCK_WIDTH; lm->rectchange.t = LMBLOCK_HEIGHT; lm->rectchange.w = 0; lm->rectchange.h = 0; //johnfitz -- use texture manager sprintf(name, "lightmap%07i",i); lm->texture = TexMgr_LoadImage (NULL, name, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, SRC_LIGHTMAP, lm->data, "", (src_offset_t)lm->data, TEXPREF_LINEAR | TEXPREF_NOPICMIP | TEXPREF_PERSIST); //johnfitz } //johnfitz -- warn about exceeding old limits //GLQuake limit was 64 textures of 128x128. Estimate how many 128x128 textures we would need //given that we are using lightmap_count of LMBLOCK_WIDTH x LMBLOCK_HEIGHT i = lightmap_count * ((LMBLOCK_WIDTH / 128) * (LMBLOCK_HEIGHT / 128)); if (i > 64) Con_DWarning("%i lightmaps exceeds standard limit of 64.\n",i); //johnfitz } /* ============================================================= VBO support ============================================================= */ 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_BuildBModelVertexBuffer Deletes gl_bmodel_vbo if it already exists, then rebuilds it with all surfaces from world + all brush models ================== */ void GL_BuildBModelVertexBuffer (void) { unsigned int numverts, varray_bytes, varray_index; int i, j; qmodel_t *m; float *varray; if (!(gl_vbo_able && gl_mtexable && gl_max_texture_units >= 3)) return; // ask GL for a name for our VBO GL_DeleteBuffersFunc (1, &gl_bmodel_vbo); GL_GenBuffersFunc (1, &gl_bmodel_vbo); // count all verts in all models numverts = 0; for (j=1 ; jname[0] == '*' || m->type != mod_brush) continue; for (i=0 ; inumsurfaces ; i++) { numverts += m->surfaces[i].numedges; } } for (j=1 ; jname[0] == '*' || m->type != mod_brush) continue; for (i=0 ; inumsurfaces ; i++) { numverts += m->surfaces[i].numedges; } } // build vertex array varray_bytes = VERTEXSIZE * sizeof(float) * numverts; varray = (float *) malloc (varray_bytes); varray_index = 0; for (j=1 ; jname[0] == '*' || m->type != mod_brush) continue; for (i=0 ; inumsurfaces ; i++) { msurface_t *s = &m->surfaces[i]; s->vbo_firstvert = varray_index; memcpy (&varray[VERTEXSIZE * varray_index], s->polys->verts, VERTEXSIZE * sizeof(float) * s->numedges); varray_index += s->numedges; } } for (j=1 ; jname[0] == '*' || m->type != mod_brush) continue; for (i=0 ; inumsurfaces ; i++) { msurface_t *s = &m->surfaces[i]; s->vbo_firstvert = varray_index; memcpy (&varray[VERTEXSIZE * varray_index], s->polys->verts, VERTEXSIZE * sizeof(float) * s->numedges); varray_index += s->numedges; } } // upload to GPU GL_BindBufferFunc (GL_ARRAY_BUFFER, gl_bmodel_vbo); GL_BufferDataFunc (GL_ARRAY_BUFFER, varray_bytes, varray, GL_STATIC_DRAW); free (varray); // invalidate the cached bindings GL_ClearBufferBindings (); } /* =============== R_AddDynamicLights =============== */ void R_AddDynamicLights (msurface_t *surf) { int lnum; int sd, td; float dist, rad, minlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; //johnfitz -- lit support via lordhavoc float cred, cgreen, cblue, brightness; unsigned *bl; //johnfitz vec3_t lightofs; //Spike: light surfaces based upon where they are now instead of their default position. int lmscale; smax = (surf->extents[0]>>surf->lmshift)+1; tmax = (surf->extents[1]>>surf->lmshift)+1; tex = surf->texinfo; lmscale = 1<lmshift; for (lnum=0 ; lnumdlightbits[lnum >> 5] & (1U << (lnum & 31)))) continue; // not lit by this light rad = cl_dlights[lnum].radius; VectorSubtract(cl_dlights[lnum].origin, currententity->origin, lightofs); dist = DotProduct (lightofs, surf->plane->normal) - surf->plane->dist; rad -= fabs(dist); minlight = cl_dlights[lnum].minlight; if (rad < minlight) continue; minlight = rad - minlight; for (i=0 ; i<3 ; i++) { impact[i] = lightofs[i] - surf->plane->normal[i]*dist; } local[0] = DotProduct (impact, tex->vecs[0]) + tex->vecs[0][3]; local[1] = DotProduct (impact, tex->vecs[1]) + tex->vecs[1][3]; local[0] -= surf->texturemins[0]; local[1] -= surf->texturemins[1]; //johnfitz -- lit support via lordhavoc bl = blocklights; cred = cl_dlights[lnum].color[0] * 256.0f; cgreen = cl_dlights[lnum].color[1] * 256.0f; cblue = cl_dlights[lnum].color[2] * 256.0f; //johnfitz for (t = 0 ; t td) dist = sd + (td>>1); else dist = td + (sd>>1); if (dist < minlight) //johnfitz -- lit support via lordhavoc { brightness = rad - dist; bl[0] += (int) (brightness * cred); bl[1] += (int) (brightness * cgreen); bl[2] += (int) (brightness * cblue); } bl += 3; //johnfitz } } } } /* =============== R_BuildLightMap -- johnfitz -- revised for lit support via lordhavoc Combine and scale multiple lightmaps into the 8.8 format in blocklights =============== */ void R_BuildLightMap (qmodel_t *model, msurface_t *surf, byte *dest, int stride) { int smax, tmax; int r,g,b; int i, j, size; byte *lightmap; unsigned scale; int maps; unsigned *bl; surf->cached_dlight = (surf->dlightframe == r_framecount); smax = (surf->extents[0]>>surf->lmshift)+1; tmax = (surf->extents[1]>>surf->lmshift)+1; size = smax*tmax; lightmap = surf->samples; if (model->lightdata) { // clear to no light memset (&blocklights[0], 0, size * 3 * sizeof (unsigned int)); //johnfitz -- lit support via lordhavoc // add all the lightmaps if (lightmap) { for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++) { scale = d_lightstylevalue[surf->styles[maps]]; surf->cached_light[maps] = scale; // 8.8 fraction //johnfitz -- lit support via lordhavoc bl = blocklights; for (i=0 ; idlightframe == r_framecount) R_AddDynamicLights (surf); } else { // set to full bright if no light data memset (&blocklights[0], 255, size * 3 * sizeof (unsigned int)); //johnfitz -- lit support via lordhavoc } // bound, invert, and shift // store: switch (gl_lightmap_format) { case GL_RGBA: stride -= smax * 4; bl = blocklights; for (i=0 ; i> 8; g = *bl++ >> 8; b = *bl++ >> 8; } else { r = *bl++ >> 7; g = *bl++ >> 7; b = *bl++ >> 7; } *dest++ = (r > 255)? 255 : r; *dest++ = (g > 255)? 255 : g; *dest++ = (b > 255)? 255 : b; *dest++ = 255; } } break; case GL_BGRA: stride -= smax * 4; bl = blocklights; for (i=0 ; i> 8; g = *bl++ >> 8; b = *bl++ >> 8; } else { r = *bl++ >> 7; g = *bl++ >> 7; b = *bl++ >> 7; } *dest++ = (b > 255)? 255 : b; *dest++ = (g > 255)? 255 : g; *dest++ = (r > 255)? 255 : r; *dest++ = 255; } } break; default: Sys_Error ("R_BuildLightMap: bad lightmap format"); } } /* =============== R_UploadLightmap -- johnfitz -- uploads the modified lightmap to opengl if necessary assumes lightmap texture is already bound =============== */ static void R_UploadLightmap(int lmap) { struct lightmap_s *lm = &lightmaps[lmap]; if (!lm->modified) return; lm->modified = false; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, lm->rectchange.t, LMBLOCK_WIDTH, lm->rectchange.h, gl_lightmap_format, GL_UNSIGNED_BYTE, lm->data+lm->rectchange.t*LMBLOCK_WIDTH*lightmap_bytes); lm->rectchange.l = LMBLOCK_WIDTH; lm->rectchange.t = LMBLOCK_HEIGHT; lm->rectchange.h = 0; lm->rectchange.w = 0; rs_dynamiclightmaps++; } void R_UploadLightmaps (void) { int lmap; if (lightmaps_latecached) { GL_BuildLightmaps (); GL_BuildBModelVertexBuffer (); lightmaps_latecached=false; } for (lmap = 0; lmap < lightmap_count; lmap++) { if (!lightmaps[lmap].modified) continue; if (!lightmaps[lmap].texture) { char name[24]; sprintf(name, "lightmap%07i",lmap); lightmaps[lmap].texture = TexMgr_LoadImage (NULL, name, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, SRC_LIGHTMAP, lightmaps[lmap].data, "", (src_offset_t)lightmaps[lmap].data, TEXPREF_LINEAR | TEXPREF_NOPICMIP | TEXPREF_PERSIST); lightmaps[lmap].modified = false; lightmaps[lmap].rectchange.l = LMBLOCK_WIDTH; lightmaps[lmap].rectchange.t = LMBLOCK_HEIGHT; lightmaps[lmap].rectchange.h = 0; lightmaps[lmap].rectchange.w = 0; } else { GL_Bind (lightmaps[lmap].texture); R_UploadLightmap(lmap); } } } /* ================ R_RebuildAllLightmaps -- johnfitz -- called when gl_overbright gets toggled ================ */ void R_RebuildAllLightmaps (void) { int i, j; qmodel_t *mod; msurface_t *fa; byte *base; if (!cl.worldmodel) // is this the correct test? return; //for each surface in each model, rebuild lightmap with new scale for (i=1; isurfaces[mod->firstmodelsurface]; for (j=0; jnummodelsurfaces; j++, fa++) { if (fa->flags & SURF_DRAWTILED) continue; base = lightmaps[fa->lightmaptexturenum].data; base += fa->light_t * LMBLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; R_BuildLightMap (mod, fa, base, LMBLOCK_WIDTH*lightmap_bytes); } } //for each lightmap, upload it for (i=0; i