diff options
Diffstat (limited to 'WinQuake/gl_rsurf.c')
| -rw-r--r-- | WinQuake/gl_rsurf.c | 1694 |
1 files changed, 1694 insertions, 0 deletions
diff --git a/WinQuake/gl_rsurf.c b/WinQuake/gl_rsurf.c new file mode 100644 index 0000000..11b5a01 --- /dev/null +++ b/WinQuake/gl_rsurf.c @@ -0,0 +1,1694 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +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_surf.c: surface-related refresh code + +#include "quakedef.h" + +int skytexturenum; + +#ifndef GL_RGBA4 +#define GL_RGBA4 0 +#endif + + +int lightmap_bytes; // 1, 2, or 4 + +int lightmap_textures; + +unsigned blocklights[18*18]; + +#define BLOCK_WIDTH 128 +#define BLOCK_HEIGHT 128 + +#define MAX_LIGHTMAPS 64 +int active_lightmaps; + +typedef struct glRect_s { + unsigned char l,t,w,h; +} glRect_t; + +glpoly_t *lightmap_polys[MAX_LIGHTMAPS]; +qboolean lightmap_modified[MAX_LIGHTMAPS]; +glRect_t lightmap_rectchange[MAX_LIGHTMAPS]; + +int allocated[MAX_LIGHTMAPS][BLOCK_WIDTH]; + +// the lightmap texture data needs to be kept in +// main memory so texsubimage can update properly +byte lightmaps[4*MAX_LIGHTMAPS*BLOCK_WIDTH*BLOCK_HEIGHT]; + +// For gl_texsort 0 +msurface_t *skychain = NULL; +msurface_t *waterchain = NULL; + +void R_RenderDynamicLightmaps (msurface_t *fa); + +/* +=============== +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; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + tex = surf->texinfo; + + for (lnum=0 ; lnum<MAX_DLIGHTS ; lnum++) + { + if ( !(surf->dlightbits & (1<<lnum) ) ) + continue; // not lit by this light + + rad = cl_dlights[lnum].radius; + dist = DotProduct (cl_dlights[lnum].origin, 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] = cl_dlights[lnum].origin[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]; + + for (t = 0 ; t<tmax ; t++) + { + td = local[1] - t*16; + if (td < 0) + td = -td; + for (s=0 ; s<smax ; s++) + { + sd = local[0] - s*16; + if (sd < 0) + sd = -sd; + if (sd > td) + dist = sd + (td>>1); + else + dist = td + (sd>>1); + if (dist < minlight) + blocklights[t*smax + s] += (rad - dist)*256; + } + } + } +} + + +/* +=============== +R_BuildLightMap + +Combine and scale multiple lightmaps into the 8.8 format in blocklights +=============== +*/ +void R_BuildLightMap (msurface_t *surf, byte *dest, int stride) +{ + int smax, tmax; + int t; + int i, j, size; + byte *lightmap; + unsigned scale; + int maps; + int lightadj[4]; + unsigned *bl; + + surf->cached_dlight = (surf->dlightframe == r_framecount); + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + size = smax*tmax; + lightmap = surf->samples; + +// set to full bright if no light data + if (r_fullbright.value || !cl.worldmodel->lightdata) + { + for (i=0 ; i<size ; i++) + blocklights[i] = 255*256; + goto store; + } + +// clear to no light + for (i=0 ; i<size ; i++) + blocklights[i] = 0; + +// add all the lightmaps + if (lightmap) + for (maps = 0 ; maps < MAXLIGHTMAPS && surf->styles[maps] != 255 ; + maps++) + { + scale = d_lightstylevalue[surf->styles[maps]]; + surf->cached_light[maps] = scale; // 8.8 fraction + for (i=0 ; i<size ; i++) + blocklights[i] += lightmap[i] * scale; + lightmap += size; // skip to next lightmap + } + +// add all the dynamic lights + if (surf->dlightframe == r_framecount) + R_AddDynamicLights (surf); + +// bound, invert, and shift +store: + switch (gl_lightmap_format) + { + case GL_RGBA: + stride -= (smax<<2); + bl = blocklights; + for (i=0 ; i<tmax ; i++, dest += stride) + { + for (j=0 ; j<smax ; j++) + { + t = *bl++; + t >>= 7; + if (t > 255) + t = 255; + dest[3] = 255-t; + dest += 4; + } + } + break; + case GL_ALPHA: + case GL_LUMINANCE: + case GL_INTENSITY: + bl = blocklights; + for (i=0 ; i<tmax ; i++, dest += stride) + { + for (j=0 ; j<smax ; j++) + { + t = *bl++; + t >>= 7; + if (t > 255) + t = 255; + dest[j] = 255-t; + } + } + break; + default: + Sys_Error ("Bad lightmap format"); + } +} + + +/* +=============== +R_TextureAnimation + +Returns the proper texture for a given time and base texture +=============== +*/ +texture_t *R_TextureAnimation (texture_t *base) +{ + int reletive; + int count; + + if (currententity->frame) + { + if (base->alternate_anims) + base = base->alternate_anims; + } + + if (!base->anim_total) + return base; + + reletive = (int)(cl.time*10) % base->anim_total; + + count = 0; + while (base->anim_min > reletive || base->anim_max <= reletive) + { + base = base->anim_next; + if (!base) + Sys_Error ("R_TextureAnimation: broken cycle"); + if (++count > 100) + Sys_Error ("R_TextureAnimation: infinite cycle"); + } + + return base; +} + + +/* +============================================================= + + BRUSH MODELS + +============================================================= +*/ + + +extern int solidskytexture; +extern int alphaskytexture; +extern float speedscale; // for top sky and bottom sky + +void DrawGLWaterPoly (glpoly_t *p); +void DrawGLWaterPolyLightmap (glpoly_t *p); + +lpMTexFUNC qglMTexCoord2fSGIS = NULL; +lpSelTexFUNC qglSelectTextureSGIS = NULL; + +qboolean mtexenabled = false; + +void GL_SelectTexture (GLenum target); + +void GL_DisableMultitexture(void) +{ + if (mtexenabled) { + glDisable(GL_TEXTURE_2D); + GL_SelectTexture(TEXTURE0_SGIS); + mtexenabled = false; + } +} + +void GL_EnableMultitexture(void) +{ + if (gl_mtexable) { + GL_SelectTexture(TEXTURE1_SGIS); + glEnable(GL_TEXTURE_2D); + mtexenabled = true; + } +} + +#if 0 +/* +================ +R_DrawSequentialPoly + +Systems that have fast state and texture changes can +just do everything as it passes with no need to sort +================ +*/ +void R_DrawSequentialPoly (msurface_t *s) +{ + glpoly_t *p; + float *v; + int i; + texture_t *t; + + // + // normal lightmaped poly + // + if (! (s->flags & (SURF_DRAWSKY|SURF_DRAWTURB|SURF_UNDERWATER) ) ) + { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + + glDisable (GL_BLEND); + + return; + } + + // + // subdivided water surface warp + // + if (s->flags & SURF_DRAWTURB) + { + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + return; + } + + // + // subdivided sky warp + // + if (s->flags & SURF_DRAWSKY) + { + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale; + + EmitSkyPolys (s); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale; + EmitSkyPolys (s); + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + + glDisable (GL_BLEND); + } + + // + // underwater warped with lightmap + // + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + DrawGLWaterPoly (p); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + DrawGLWaterPolyLightmap (p); + glDisable (GL_BLEND); +} +#else +/* +================ +R_DrawSequentialPoly + +Systems that have fast state and texture changes can +just do everything as it passes with no need to sort +================ +*/ +void R_DrawSequentialPoly (msurface_t *s) +{ + glpoly_t *p; + float *v; + int i; + texture_t *t; + vec3_t nv, dir; + float ss, ss2, length; + float s1, t1; + glRect_t *theRect; + + // + // normal lightmaped poly + // + + if (! (s->flags & (SURF_DRAWSKY|SURF_DRAWTURB|SURF_UNDERWATER) ) ) + { + R_RenderDynamicLightmaps (s); + if (gl_mtexable) { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + // Binds world to texture env 0 + GL_SelectTexture(TEXTURE0_SGIS); + GL_Bind (t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + // Binds lightmap to texenv 1 + GL_EnableMultitexture(); // Same as SelectTexture (TEXTURE1) + GL_Bind (lightmap_textures + s->lightmaptexturenum); + i = s->lightmaptexturenum; + if (lightmap_modified[i]) + { + lightmap_modified[i] = false; + theRect = &lightmap_rectchange[i]; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + glBegin(GL_POLYGON); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (TEXTURE0_SGIS, v[3], v[4]); + qglMTexCoord2fSGIS (TEXTURE1_SGIS, v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + return; + } else { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + + glDisable (GL_BLEND); + } + + return; + } + + // + // subdivided water surface warp + // + + if (s->flags & SURF_DRAWTURB) + { + GL_DisableMultitexture(); + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + return; + } + + // + // subdivided sky warp + // + if (s->flags & SURF_DRAWSKY) + { + GL_DisableMultitexture(); + GL_Bind (solidskytexture); + speedscale = realtime*8; + speedscale -= (int)speedscale & ~127; + + EmitSkyPolys (s); + + glEnable (GL_BLEND); + GL_Bind (alphaskytexture); + speedscale = realtime*16; + speedscale -= (int)speedscale & ~127; + EmitSkyPolys (s); + + glDisable (GL_BLEND); + return; + } + + // + // underwater warped with lightmap + // + R_RenderDynamicLightmaps (s); + if (gl_mtexable) { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_SelectTexture(TEXTURE0_SGIS); + GL_Bind (t->gl_texturenum); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + GL_EnableMultitexture(); + GL_Bind (lightmap_textures + s->lightmaptexturenum); + i = s->lightmaptexturenum; + if (lightmap_modified[i]) + { + lightmap_modified[i] = false; + theRect = &lightmap_rectchange[i]; + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND); + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + qglMTexCoord2fSGIS (TEXTURE0_SGIS, v[3], v[4]); + qglMTexCoord2fSGIS (TEXTURE1_SGIS, v[5], v[6]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); + + } else { + p = s->polys; + + t = R_TextureAnimation (s->texinfo->texture); + GL_Bind (t->gl_texturenum); + DrawGLWaterPoly (p); + + GL_Bind (lightmap_textures + s->lightmaptexturenum); + glEnable (GL_BLEND); + DrawGLWaterPolyLightmap (p); + glDisable (GL_BLEND); + } +} +#endif + + +/* +================ +DrawGLWaterPoly + +Warp the vertex coordinates +================ +*/ +void DrawGLWaterPoly (glpoly_t *p) +{ + int i; + float *v; + float s, t, os, ot; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} + +void DrawGLWaterPolyLightmap (glpoly_t *p) +{ + int i; + float *v; + float s, t, os, ot; + vec3_t nv; + + GL_DisableMultitexture(); + + glBegin (GL_TRIANGLE_FAN); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + + nv[0] = v[0] + 8*sin(v[1]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[1] = v[1] + 8*sin(v[0]*0.05+realtime)*sin(v[2]*0.05+realtime); + nv[2] = v[2]; + + glVertex3fv (nv); + } + glEnd (); +} + +/* +================ +DrawGLPoly +================ +*/ +void DrawGLPoly (glpoly_t *p) +{ + int i; + float *v; + + glBegin (GL_POLYGON); + v = p->verts[0]; + for (i=0 ; i<p->numverts ; i++, v+= VERTEXSIZE) + { + glTexCoord2f (v[3], v[4]); + glVertex3fv (v); + } + glEnd (); +} + + +/* +================ +R_BlendLightmaps +================ +*/ +void R_BlendLightmaps (void) +{ + int i, j; + glpoly_t *p; + float *v; + glRect_t *theRect; + + if (r_fullbright.value) + return; + if (!gl_texsort.value) + return; + + glDepthMask (0); // don't bother writing Z + + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glColor4f (0,0,0,1); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + if (!r_lightmap.value) + { + glEnable (GL_BLEND); + } + + for (i=0 ; i<MAX_LIGHTMAPS ; i++) + { + p = lightmap_polys[i]; + if (!p) + continue; + GL_Bind(lightmap_textures+i); + if (lightmap_modified[i]) + { + lightmap_modified[i] = false; + theRect = &lightmap_rectchange[i]; +// glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes +// , BLOCK_WIDTH, BLOCK_HEIGHT, 0, +// gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); +// glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes +// , BLOCK_WIDTH, theRect->h, 0, +// gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+(i*BLOCK_HEIGHT+theRect->t)*BLOCK_WIDTH*lightmap_bytes); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, theRect->t, + BLOCK_WIDTH, theRect->h, gl_lightmap_format, GL_UNSIGNED_BYTE, + lightmaps+(i* BLOCK_HEIGHT + theRect->t) *BLOCK_WIDTH*lightmap_bytes); + theRect->l = BLOCK_WIDTH; + theRect->t = BLOCK_HEIGHT; + theRect->h = 0; + theRect->w = 0; + } + for ( ; p ; p=p->chain) + { + if (p->flags & SURF_UNDERWATER) + DrawGLWaterPolyLightmap (p); + else + { + glBegin (GL_POLYGON); + v = p->verts[0]; + for (j=0 ; j<p->numverts ; j++, v+= VERTEXSIZE) + { + glTexCoord2f (v[5], v[6]); + glVertex3fv (v); + } + glEnd (); + } + } + } + + glDisable (GL_BLEND); + if (gl_lightmap_format == GL_LUMINANCE) + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + else if (gl_lightmap_format == GL_INTENSITY) + { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glColor4f (1,1,1,1); + } + + glDepthMask (1); // back to normal Z buffering +} + +/* +================ +R_RenderBrushPoly +================ +*/ +void R_RenderBrushPoly (msurface_t *fa) +{ + texture_t *t; + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + + c_brush_polys++; + + if (fa->flags & SURF_DRAWSKY) + { // warp texture, no lightmaps + EmitBothSkyLayers (fa); + return; + } + + t = R_TextureAnimation (fa->texinfo->texture); + GL_Bind (t->gl_texturenum); + + if (fa->flags & SURF_DRAWTURB) + { // warp texture, no lightmaps + EmitWaterPolys (fa); + return; + } + + if (fa->flags & SURF_UNDERWATER) + DrawGLWaterPoly (fa->polys); + else + DrawGLPoly (fa->polys); + + // add the poly to the proper lightmap chain + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + // check for lightmap modification + for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ; + 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) + { + lightmap_modified[fa->lightmaptexturenum] = true; + theRect = &lightmap_rectchange[fa->lightmaptexturenum]; + 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]>>4)+1; + tmax = (fa->extents[1]>>4)+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 = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); + } + } +} + +/* +================ +R_RenderDynamicLightmaps +Multitexture +================ +*/ +void R_RenderDynamicLightmaps (msurface_t *fa) +{ + texture_t *t; + byte *base; + int maps; + glRect_t *theRect; + int smax, tmax; + + c_brush_polys++; + + if (fa->flags & ( SURF_DRAWSKY | SURF_DRAWTURB) ) + return; + + fa->polys->chain = lightmap_polys[fa->lightmaptexturenum]; + lightmap_polys[fa->lightmaptexturenum] = fa->polys; + + // check for lightmap modification + for (maps = 0 ; maps < MAXLIGHTMAPS && fa->styles[maps] != 255 ; + 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) + { + lightmap_modified[fa->lightmaptexturenum] = true; + theRect = &lightmap_rectchange[fa->lightmaptexturenum]; + 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]>>4)+1; + tmax = (fa->extents[1]>>4)+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 = lightmaps + fa->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += fa->light_t * BLOCK_WIDTH * lightmap_bytes + fa->light_s * lightmap_bytes; + R_BuildLightMap (fa, base, BLOCK_WIDTH*lightmap_bytes); + } + } +} + +/* +================ +R_MirrorChain +================ +*/ +void R_MirrorChain (msurface_t *s) +{ + if (mirror) + return; + mirror = true; + mirror_plane = s->plane; +} + + +#if 0 +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (r_wateralpha.value == 1.0) + return; + + // + // go back to the world matrix + // + glLoadMatrixf (r_world_matrix); + + glEnable (GL_BLEND); + glColor4f (1,1,1,r_wateralpha.value); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + for (i=0 ; i<cl.worldmodel->numtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if ( !(s->flags & SURF_DRAWTURB) ) + continue; + + // set modulate mode explicitly + GL_Bind (t->gl_texturenum); + + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + + t->texturechain = NULL; + } + + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); +} +#else +/* +================ +R_DrawWaterSurfaces +================ +*/ +void R_DrawWaterSurfaces (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (r_wateralpha.value == 1.0 && gl_texsort.value) + return; + + // + // go back to the world matrix + // + + glLoadMatrixf (r_world_matrix); + + if (r_wateralpha.value < 1.0) { + glEnable (GL_BLEND); + glColor4f (1,1,1,r_wateralpha.value); + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + + if (!gl_texsort.value) { + if (!waterchain) + return; + + for ( s = waterchain ; s ; s=s->texturechain) { + GL_Bind (s->texinfo->texture->gl_texturenum); + EmitWaterPolys (s); + } + + waterchain = NULL; + } else { + + for (i=0 ; i<cl.worldmodel->numtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if ( !(s->flags & SURF_DRAWTURB ) ) + continue; + + // set modulate mode explicitly + + GL_Bind (t->gl_texturenum); + + for ( ; s ; s=s->texturechain) + EmitWaterPolys (s); + + t->texturechain = NULL; + } + + } + + if (r_wateralpha.value < 1.0) { + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + + glColor4f (1,1,1,1); + glDisable (GL_BLEND); + } + +} + +#endif + +/* +================ +DrawTextureChains +================ +*/ +void DrawTextureChains (void) +{ + int i; + msurface_t *s; + texture_t *t; + + if (!gl_texsort.value) { + GL_DisableMultitexture(); + + if (skychain) { + R_DrawSkyChain(skychain); + skychain = NULL; + } + + return; + } + + for (i=0 ; i<cl.worldmodel->numtextures ; i++) + { + t = cl.worldmodel->textures[i]; + if (!t) + continue; + s = t->texturechain; + if (!s) + continue; + if (i == skytexturenum) + R_DrawSkyChain (s); + else if (i == mirrortexturenum && r_mirroralpha.value != 1.0) + { + R_MirrorChain (s); + continue; + } + else + { + if ((s->flags & SURF_DRAWTURB) && r_wateralpha.value != 1.0) + continue; // draw translucent water later + for ( ; s ; s=s->texturechain) + R_RenderBrushPoly (s); + } + + t->texturechain = NULL; + } +} + +/* +================= +R_DrawBrushModel +================= +*/ +void R_DrawBrushModel (entity_t *e) +{ + int j, k; + vec3_t mins, maxs; + int i, numsurfaces; + msurface_t *psurf; + float dot; + mplane_t *pplane; + model_t *clmodel; + qboolean rotated; + + currententity = e; + currenttexture = -1; + + clmodel = e->model; + + if (e->angles[0] || e->angles[1] || e->angles[2]) + { + rotated = true; + for (i=0 ; i<3 ; i++) + { + mins[i] = e->origin[i] - clmodel->radius; + maxs[i] = e->origin[i] + clmodel->radius; + } + } + else + { + rotated = false; + VectorAdd (e->origin, clmodel->mins, mins); + VectorAdd (e->origin, clmodel->maxs, maxs); + } + + if (R_CullBox (mins, maxs)) + return; + + glColor3f (1,1,1); + memset (lightmap_polys, 0, sizeof(lightmap_polys)); + + VectorSubtract (r_refdef.vieworg, e->origin, modelorg); + if (rotated) + { + 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; + + R_MarkLights (&cl_dlights[k], 1<<k, + clmodel->nodes + clmodel->hulls[0].firstclipnode); + } + } + + glPushMatrix (); +e->angles[0] = -e->angles[0]; // stupid quake bug + R_RotateForEntity (e); +e->angles[0] = -e->angles[0]; // stupid quake bug + + // + // draw texture + // + for (i=0 ; i<clmodel->nummodelsurfaces ; i++, psurf++) + { + // find which side of the node we are on + pplane = psurf->plane; + + dot = DotProduct (modelorg, pplane->normal) - pplane->dist; + + // draw the polygon + if (((psurf->flags & SURF_PLANEBACK) && (dot < -BACKFACE_EPSILON)) || + (!(psurf->flags & SURF_PLANEBACK) && (dot > BACKFACE_EPSILON))) + { + if (gl_texsort.value) + R_RenderBrushPoly (psurf); + else + R_DrawSequentialPoly (psurf); + } + } + + R_BlendLightmaps (); + + glPopMatrix (); +} + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + +/* +================ +R_RecursiveWorldNode +================ +*/ +void R_RecursiveWorldNode (mnode_t *node) +{ + int i, c, side, *pindex; + vec3_t acceptpt, rejectpt; + mplane_t *plane; + msurface_t *surf, **mark; + mleaf_t *pleaf; + double d, dot; + vec3_t mins, maxs; + + if (node->contents == CONTENTS_SOLID) + return; // solid + + if (node->visframe != r_visframecount) + return; + if (R_CullBox (node->minmaxs, node->minmaxs+3)) + return; + +// if a leaf node, draw stuff + if (node->contents < 0) + { + pleaf = (mleaf_t *)node; + + mark = pleaf->firstmarksurface; + c = pleaf->nummarksurfaces; + + if (c) + { + do + { + (*mark)->visframe = r_framecount; + mark++; + } while (--c); + } + + // deal with model fragments in this leaf + if (pleaf->efrags) + R_StoreEfrags (&pleaf->efrags); + + return; + } + +// node is just a decision point, so go down the apropriate sides + +// find which side of the node we are on + plane = node->plane; + + switch (plane->type) + { + case PLANE_X: + dot = modelorg[0] - plane->dist; + break; + case PLANE_Y: + dot = modelorg[1] - plane->dist; + break; + case PLANE_Z: + dot = modelorg[2] - plane->dist; + break; + default: + dot = DotProduct (modelorg, plane->normal) - plane->dist; + break; + } + + if (dot >= 0) + side = 0; + else + side = 1; + +// recurse down the children, front side first + R_RecursiveWorldNode (node->children[side]); + +// draw stuff + c = node->numsurfaces; + + if (c) + { + surf = cl.worldmodel->surfaces + node->firstsurface; + + if (dot < 0 -BACKFACE_EPSILON) + side = SURF_PLANEBACK; + else if (dot > BACKFACE_EPSILON) + side = 0; + { + for ( ; c ; c--, surf++) + { + if (surf->visframe != r_framecount) + continue; + + // don't backface underwater surfaces, because they warp + if ( !(surf->flags & SURF_UNDERWATER) && ( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)) ) + continue; // wrong side + + // if sorting by texture, just store it out + if (gl_texsort.value) + { + if (!mirror + || surf->texinfo->texture != cl.worldmodel->textures[mirrortexturenum]) + { + surf->texturechain = surf->texinfo->texture->texturechain; + surf->texinfo->texture->texturechain = surf; + } + } else if (surf->flags & SURF_DRAWSKY) { + surf->texturechain = skychain; + skychain = surf; + } else if (surf->flags & SURF_DRAWTURB) { + surf->texturechain = waterchain; + waterchain = surf; + } else + R_DrawSequentialPoly (surf); + + } + } + + } + +// recurse down the back side + R_RecursiveWorldNode (node->children[!side]); +} + + + +/* +============= +R_DrawWorld +============= +*/ +void R_DrawWorld (void) +{ + entity_t ent; + int i; + + memset (&ent, 0, sizeof(ent)); + ent.model = cl.worldmodel; + + VectorCopy (r_refdef.vieworg, modelorg); + + currententity = &ent; + currenttexture = -1; + + glColor3f (1,1,1); + memset (lightmap_polys, 0, sizeof(lightmap_polys)); +#ifdef QUAKE2 + R_ClearSkyBox (); +#endif + + R_RecursiveWorldNode (cl.worldmodel->nodes); + + DrawTextureChains (); + + R_BlendLightmaps (); + +#ifdef QUAKE2 + R_DrawSkyBox (); +#endif +} + + +/* +=============== +R_MarkLeaves +=============== +*/ +void R_MarkLeaves (void) +{ + byte *vis; + mnode_t *node; + int i; + byte solid[4096]; + + if (r_oldviewleaf == r_viewleaf && !r_novis.value) + return; + + if (mirror) + return; + + r_visframecount++; + r_oldviewleaf = r_viewleaf; + + if (r_novis.value) + { + vis = solid; + memset (solid, 0xff, (cl.worldmodel->numleafs+7)>>3); + } + else + vis = Mod_LeafPVS (r_viewleaf, cl.worldmodel); + + for (i=0 ; i<cl.worldmodel->numleafs ; i++) + { + if (vis[i>>3] & (1<<(i&7))) + { + node = (mnode_t *)&cl.worldmodel->leafs[i+1]; + do + { + if (node->visframe == r_visframecount) + break; + node->visframe = r_visframecount; + node = node->parent; + } while (node); + } + } +} + + + +/* +============================================================================= + + LIGHTMAP ALLOCATION + +============================================================================= +*/ + +// 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 bestx; + int texnum; + + for (texnum=0 ; texnum<MAX_LIGHTMAPS ; texnum++) + { + best = BLOCK_HEIGHT; + + for (i=0 ; i<BLOCK_WIDTH-w ; i++) + { + best2 = 0; + + for (j=0 ; j<w ; j++) + { + if (allocated[texnum][i+j] >= best) + break; + if (allocated[texnum][i+j] > best2) + best2 = allocated[texnum][i+j]; + } + if (j == w) + { // this is a valid spot + *x = i; + *y = best = best2; + } + } + + if (best + h > BLOCK_HEIGHT) + continue; + + for (i=0 ; i<w ; i++) + allocated[texnum][*x + i] = best + h; + + return texnum; + } + + Sys_Error ("AllocBlock: full"); +} + + +mvertex_t *r_pcurrentvertbase; +model_t *currentmodel; + +int nColinElim; + +/* +================ +BuildSurfaceDisplayList +================ +*/ +void BuildSurfaceDisplayList (msurface_t *fa) +{ + int i, lindex, lnumverts, s_axis, t_axis; + float dist, lastdist, lzi, scale, u, v, frac; + unsigned mask; + vec3_t local, transformed; + medge_t *pedges, *r_pedge; + mplane_t *pplane; + int vertpage, newverts, newpage, lastvert; + qboolean visible; + float *vec; + float s, t; + glpoly_t *poly; + +// reconstruct the polygon + pedges = currentmodel->edges; + lnumverts = fa->numedges; + vertpage = 0; + + // + // draw texture + // + poly = Hunk_Alloc (sizeof(glpoly_t) + (lnumverts-4) * VERTEXSIZE*sizeof(float)); + poly->next = fa->polys; + poly->flags = fa->flags; + 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*16; + s += 8; + s /= BLOCK_WIDTH*16; //fa->texinfo->texture->width; + + t = DotProduct (vec, fa->texinfo->vecs[1]) + fa->texinfo->vecs[1][3]; + t -= fa->texturemins[1]; + t += fa->light_t*16; + t += 8; + t /= BLOCK_HEIGHT*16; //fa->texinfo->texture->height; + + poly->verts[i][5] = s; + poly->verts[i][6] = t; + } + + // + // remove co-linear points - Ed + // + if (!gl_keeptjunctions.value && !(fa->flags & SURF_UNDERWATER) ) + { + for (i = 0 ; i < lnumverts ; ++i) + { + vec3_t v1, v2; + float *prev, *this, *next; + float f; + + prev = poly->verts[(i + lnumverts - 1) % lnumverts]; + this = poly->verts[i]; + next = poly->verts[(i + 1) % lnumverts]; + + VectorSubtract( this, prev, v1 ); + VectorNormalize( v1 ); + VectorSubtract( next, prev, v2 ); + VectorNormalize( v2 ); + + // skip co-linear points + #define COLINEAR_EPSILON 0.001 + if ((fabs( v1[0] - v2[0] ) <= COLINEAR_EPSILON) && + (fabs( v1[1] - v2[1] ) <= COLINEAR_EPSILON) && + (fabs( v1[2] - v2[2] ) <= COLINEAR_EPSILON)) + { + int j; + for (j = i + 1; j < lnumverts; ++j) + { + int k; + for (k = 0; k < VERTEXSIZE; ++k) + poly->verts[j - 1][k] = poly->verts[j][k]; + } + --lnumverts; + ++nColinElim; + // retry next vertex next time, which is now current vertex + --i; + } + } + } + poly->numverts = lnumverts; + +} + +/* +======================== +GL_CreateSurfaceLightmap +======================== +*/ +void GL_CreateSurfaceLightmap (msurface_t *surf) +{ + int smax, tmax, s, t, l, i; + byte *base; + + if (surf->flags & (SURF_DRAWSKY|SURF_DRAWTURB)) + return; + + smax = (surf->extents[0]>>4)+1; + tmax = (surf->extents[1]>>4)+1; + + surf->lightmaptexturenum = AllocBlock (smax, tmax, &surf->light_s, &surf->light_t); + base = lightmaps + surf->lightmaptexturenum*lightmap_bytes*BLOCK_WIDTH*BLOCK_HEIGHT; + base += (surf->light_t * BLOCK_WIDTH + surf->light_s) * lightmap_bytes; + R_BuildLightMap (surf, base, BLOCK_WIDTH*lightmap_bytes); +} + + +/* +================== +GL_BuildLightmaps + +Builds the lightmap texture +with all the surfaces from all brush models +================== +*/ +void GL_BuildLightmaps (void) +{ + int i, j; + model_t *m; + extern qboolean isPermedia; + + memset (allocated, 0, sizeof(allocated)); + + r_framecount = 1; // no dlightcache + + if (!lightmap_textures) + { + lightmap_textures = texture_extension_number; + texture_extension_number += MAX_LIGHTMAPS; + } + + gl_lightmap_format = GL_LUMINANCE; + // default differently on the Permedia + if (isPermedia) + gl_lightmap_format = GL_RGBA; + + if (COM_CheckParm ("-lm_1")) + gl_lightmap_format = GL_LUMINANCE; + if (COM_CheckParm ("-lm_a")) + gl_lightmap_format = GL_ALPHA; + if (COM_CheckParm ("-lm_i")) + gl_lightmap_format = GL_INTENSITY; + if (COM_CheckParm ("-lm_2")) + gl_lightmap_format = GL_RGBA4; + if (COM_CheckParm ("-lm_4")) + gl_lightmap_format = GL_RGBA; + + switch (gl_lightmap_format) + { + case GL_RGBA: + lightmap_bytes = 4; + break; + case GL_RGBA4: + lightmap_bytes = 2; + break; + case GL_LUMINANCE: + case GL_INTENSITY: + case GL_ALPHA: + lightmap_bytes = 1; + break; + } + + for (j=1 ; j<MAX_MODELS ; j++) + { + m = cl.model_precache[j]; + if (!m) + break; + if (m->name[0] == '*') + continue; + r_pcurrentvertbase = m->vertexes; + currentmodel = m; + for (i=0 ; i<m->numsurfaces ; i++) + { + GL_CreateSurfaceLightmap (m->surfaces + i); + if ( m->surfaces[i].flags & SURF_DRAWTURB ) + continue; +#ifndef QUAKE2 + if ( m->surfaces[i].flags & SURF_DRAWSKY ) + continue; +#endif + BuildSurfaceDisplayList (m->surfaces + i); + } + } + + if (!gl_texsort.value) + GL_SelectTexture(TEXTURE1_SGIS); + + // + // upload all lightmaps that were filled + // + for (i=0 ; i<MAX_LIGHTMAPS ; i++) + { + if (!allocated[i][0]) + break; // no more used + lightmap_modified[i] = false; + lightmap_rectchange[i].l = BLOCK_WIDTH; + lightmap_rectchange[i].t = BLOCK_HEIGHT; + lightmap_rectchange[i].w = 0; + lightmap_rectchange[i].h = 0; + GL_Bind(lightmap_textures + i); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D (GL_TEXTURE_2D, 0, lightmap_bytes + , BLOCK_WIDTH, BLOCK_HEIGHT, 0, + gl_lightmap_format, GL_UNSIGNED_BYTE, lightmaps+i*BLOCK_WIDTH*BLOCK_HEIGHT*lightmap_bytes); + } + + if (!gl_texsort.value) + GL_SelectTexture(TEXTURE0_SGIS); + +} + |
