mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2024-11-10 15:31:39 +00:00
1373 lines
33 KiB
C
1373 lines
33 KiB
C
/*
|
|
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;
|
|
|
|
#define MAX_SANITY_LIGHTMAPS (1u<<20)
|
|
struct lightmap_s *lightmap;
|
|
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 ; i<p->numverts ; 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 ; i<p->numverts ; 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 ; i<s->polys->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 ; i<s->polys->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 ; i<s->polys->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 ; i<s->polys->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 ; i<s->polys->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 ; k<MAX_DLIGHTS ; k++)
|
|
{
|
|
if ((cl_dlights[k].die < cl.time) ||
|
|
(!cl_dlights[k].radius))
|
|
continue;
|
|
|
|
VectorSubtract(cl_dlights[k].origin, e->origin, 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 ; i<clmodel->nummodelsurfaces ; 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);
|
|
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 ; i<clmodel->nummodelsurfaces ; 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 = lightmap[fa->lightmaptexturenum].polys;
|
|
lightmap[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 = &lightmap[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<MAX_SANITY_LIGHTMAPS ; texnum++)
|
|
{
|
|
if (texnum == lightmap_count)
|
|
{
|
|
lightmap_count++;
|
|
lightmap = (struct lightmap_s *) realloc(lightmap, sizeof(*lightmap)*lightmap_count);
|
|
memset(&lightmap[texnum], 0, sizeof(lightmap[texnum]));
|
|
/* FIXME: we leave 'gaps' in malloc()ed data, CRC_Block() later accesses
|
|
* that uninitialized data and valgrind complains for it. use calloc() ? */
|
|
lightmap[texnum].data = (byte *) malloc(4*LMBLOCK_WIDTH*LMBLOCK_HEIGHT);
|
|
//as we're only tracking one texture, we don't need multiple copies of allocated any more.
|
|
memset(allocated, 0, sizeof(allocated));
|
|
}
|
|
best = LMBLOCK_HEIGHT;
|
|
|
|
for (i=0 ; i<LMBLOCK_WIDTH-w ; i++)
|
|
{
|
|
best2 = 0;
|
|
|
|
for (j=0 ; j<w ; j++)
|
|
{
|
|
if (allocated[i+j] >= 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 ; i<w ; i++)
|
|
allocated[*x + i] = best + h;
|
|
|
|
last_lightmap_allocated = texnum;
|
|
return texnum;
|
|
}
|
|
|
|
Sys_Error ("AllocBlock: full");
|
|
return 0; //johnfitz -- shut up compiler
|
|
}
|
|
|
|
|
|
mvertex_t *r_pcurrentvertbase;
|
|
qmodel_t *currentmodel;
|
|
|
|
int nColinElim;
|
|
|
|
/*
|
|
========================
|
|
GL_CreateSurfaceLightmap
|
|
========================
|
|
*/
|
|
void GL_CreateSurfaceLightmap (qmodel_t *model, msurface_t *surf)
|
|
{
|
|
int smax, tmax;
|
|
byte *base;
|
|
|
|
smax = (surf->extents[0]>>surf->lmshift)+1;
|
|
tmax = (surf->extents[1]>>surf->lmshift)+1;
|
|
|
|
surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t);
|
|
base = lightmap[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<<fa->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 ; i<lnumverts ; i++)
|
|
{
|
|
lindex = currentmodel->surfedges[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;
|
|
|
|
//
|
|
// 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 ; i<m->numsurfaces ; 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++)
|
|
free(lightmap[i].data);
|
|
free(lightmap);
|
|
lightmap = 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 ; j<MAX_MODELS ; j++)
|
|
{
|
|
m = cl.model_precache[j];
|
|
if (!m)
|
|
break;
|
|
GL_BuildModel(m);
|
|
}
|
|
for (j=1 ; j<MAX_MODELS ; j++)
|
|
{
|
|
m = cl.model_precache_csqc[j];
|
|
if (!m)
|
|
break;
|
|
GL_BuildModel(m);
|
|
}
|
|
|
|
//
|
|
// upload all lightmaps that were filled
|
|
//
|
|
for (i=0; i<lightmap_count; i++)
|
|
{
|
|
lm = &lightmap[i];
|
|
lm->modified = 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 (cl.worldmodel, name, LMBLOCK_WIDTH, LMBLOCK_HEIGHT,
|
|
SRC_LIGHTMAP, lm->data, "", (src_offset_t)lm->data, TEXPREF_LINEAR | TEXPREF_NOPICMIP);
|
|
//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 ; j<MAX_MODELS ; j++)
|
|
{
|
|
m = cl.model_precache[j];
|
|
if (!m || m->name[0] == '*' || m->type != mod_brush)
|
|
continue;
|
|
|
|
for (i=0 ; i<m->numsurfaces ; i++)
|
|
{
|
|
numverts += m->surfaces[i].numedges;
|
|
}
|
|
}
|
|
for (j=1 ; j<MAX_MODELS ; j++)
|
|
{
|
|
m = cl.model_precache_csqc[j];
|
|
if (!m || m->name[0] == '*' || m->type != mod_brush)
|
|
continue;
|
|
|
|
for (i=0 ; i<m->numsurfaces ; 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 ; j<MAX_MODELS ; j++)
|
|
{
|
|
m = cl.model_precache[j];
|
|
if (!m || m->name[0] == '*' || m->type != mod_brush)
|
|
continue;
|
|
|
|
for (i=0 ; i<m->numsurfaces ; 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 ; j<MAX_MODELS ; j++)
|
|
{
|
|
m = cl.model_precache_csqc[j];
|
|
if (!m || m->name[0] == '*' || m->type != mod_brush)
|
|
continue;
|
|
|
|
for (i=0 ; i<m->numsurfaces ; 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<<surf->lmshift;
|
|
|
|
for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++)
|
|
{
|
|
if (! (surf->dlightbits[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<tmax ; t++)
|
|
{
|
|
td = local[1] - t*lmscale;
|
|
if (td < 0)
|
|
td = -td;
|
|
for (s=0 ; s<smax ; s++)
|
|
{
|
|
sd = local[0] - s*lmscale;
|
|
if (sd < 0)
|
|
sd = -sd;
|
|
if (sd > 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 ; i<size ; i++)
|
|
{
|
|
*bl++ += *lightmap++ * scale;
|
|
*bl++ += *lightmap++ * scale;
|
|
*bl++ += *lightmap++ * scale;
|
|
}
|
|
//johnfitz
|
|
}
|
|
}
|
|
|
|
// add all the dynamic lights
|
|
if (surf->dlightframe == 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<tmax ; i++, dest += stride)
|
|
{
|
|
for (j=0 ; j<smax ; j++)
|
|
{
|
|
if (gl_overbright.value)
|
|
{
|
|
r = *bl++ >> 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<tmax ; i++, dest += stride)
|
|
{
|
|
for (j=0 ; j<smax ; j++)
|
|
{
|
|
if (gl_overbright.value)
|
|
{
|
|
r = *bl++ >> 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 = &lightmap[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;
|
|
|
|
for (lmap = 0; lmap < lightmap_count; lmap++)
|
|
{
|
|
if (!lightmap[lmap].modified)
|
|
continue;
|
|
|
|
GL_Bind (lightmap[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; i<MAX_MODELS; i++)
|
|
{
|
|
if (!(mod = cl.model_precache[i]))
|
|
continue;
|
|
fa = &mod->surfaces[mod->firstmodelsurface];
|
|
for (j=0; j<mod->nummodelsurfaces; j++, fa++)
|
|
{
|
|
if (fa->flags & SURF_DRAWTILED)
|
|
continue;
|
|
base = lightmap[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<lightmap_count; i++)
|
|
{
|
|
GL_Bind (lightmap[i].texture);
|
|
glTexSubImage2D (GL_TEXTURE_2D, 0, 0, 0, LMBLOCK_WIDTH, LMBLOCK_HEIGHT, gl_lightmap_format,
|
|
GL_UNSIGNED_BYTE, lightmap[i].data);
|
|
}
|
|
}
|