/* * Copyright (C) 1997-2001 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_light.c */ #include "header/local.h" int r_dlightframecount; #define DLIGHT_CUTOFF 64 void R_RenderDlight ( dlight_t *light ) { int i, j; float a; vec3_t v; float rad; rad = light->intensity * 0.35; VectorSubtract( light->origin, r_origin, v ); qglBegin( GL_TRIANGLE_FAN ); qglColor3f( light->color [ 0 ] * 0.2, light->color [ 1 ] * 0.2, light->color [ 2 ] * 0.2 ); for ( i = 0; i < 3; i++ ) { v [ i ] = light->origin [ i ] - vpn [ i ] * rad; } qglVertex3fv( v ); qglColor3f( 0, 0, 0 ); for ( i = 16; i >= 0; i-- ) { a = i / 16.0 * M_PI * 2; for ( j = 0; j < 3; j++ ) { v [ j ] = light->origin [ j ] + vright [ j ] * cos( a ) * rad + vup [ j ] * sin( a ) * rad; } qglVertex3fv( v ); } qglEnd(); } void R_RenderDlights ( void ) { int i; dlight_t *l; if ( !gl_flashblend->value ) { return; } /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; qglDepthMask( 0 ); qglDisable( GL_TEXTURE_2D ); qglShadeModel( GL_SMOOTH ); qglEnable( GL_BLEND ); qglBlendFunc( GL_ONE, GL_ONE ); l = r_newrefdef.dlights; for ( i = 0; i < r_newrefdef.num_dlights; i++, l++ ) { R_RenderDlight( l ); } qglColor3f( 1, 1, 1 ); qglDisable( GL_BLEND ); qglEnable( GL_TEXTURE_2D ); qglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); qglDepthMask( 1 ); } void R_MarkLights ( dlight_t *light, int bit, mnode_t *node ) { cplane_t *splitplane; float dist; msurface_t *surf; int i; int sidebit; if ( node->contents != -1 ) { return; } splitplane = node->plane; dist = DotProduct( light->origin, splitplane->normal ) - splitplane->dist; if ( dist > light->intensity - DLIGHT_CUTOFF ) { R_MarkLights( light, bit, node->children [ 0 ] ); return; } if ( dist < -light->intensity + DLIGHT_CUTOFF ) { R_MarkLights( light, bit, node->children [ 1 ] ); return; } /* mark the polygons */ surf = r_worldmodel->surfaces + node->firstsurface; for ( i = 0; i < node->numsurfaces; i++, surf++ ) { dist = DotProduct (light->origin, surf->plane->normal) - surf->plane->dist; if (dist >= 0) sidebit = 0; else sidebit = SURF_PLANEBACK; if ( (surf->flags & SURF_PLANEBACK) != sidebit ) continue; if ( surf->dlightframe != r_dlightframecount ) { surf->dlightbits = 0; surf->dlightframe = r_dlightframecount; } surf->dlightbits |= bit; } R_MarkLights( light, bit, node->children [ 0 ] ); R_MarkLights( light, bit, node->children [ 1 ] ); } void R_PushDlights ( void ) { int i; dlight_t *l; if ( gl_flashblend->value ) { return; } /* because the count hasn't advanced yet for this frame */ r_dlightframecount = r_framecount + 1; l = r_newrefdef.dlights; for ( i = 0; i < r_newrefdef.num_dlights; i++, l++ ) { R_MarkLights( l, 1 << i, r_worldmodel->nodes ); } } vec3_t pointcolor; cplane_t *lightplane; /* used as shadow plane */ vec3_t lightspot; int RecursiveLightPoint ( mnode_t *node, vec3_t start, vec3_t end ) { float front, back, frac; int side; cplane_t *plane; vec3_t mid; msurface_t *surf; int s, t, ds, dt; int i; mtexinfo_t *tex; byte *lightmap; int maps; int r; if ( node->contents != -1 ) { return ( -1 ); /* didn't hit anything */ } /* calculate mid point */ plane = node->plane; front = DotProduct( start, plane->normal ) - plane->dist; back = DotProduct( end, plane->normal ) - plane->dist; side = front < 0; if ( ( back < 0 ) == side ) { return ( RecursiveLightPoint( node->children [ side ], start, end ) ); } frac = front / ( front - back ); mid [ 0 ] = start [ 0 ] + ( end [ 0 ] - start [ 0 ] ) * frac; mid [ 1 ] = start [ 1 ] + ( end [ 1 ] - start [ 1 ] ) * frac; mid [ 2 ] = start [ 2 ] + ( end [ 2 ] - start [ 2 ] ) * frac; /* go down front side */ r = RecursiveLightPoint( node->children [ side ], start, mid ); if ( r >= 0 ) { return ( r ); /* hit something */ } if ( ( back < 0 ) == side ) { return ( -1 ); /* didn't hit anuthing */ } /* check for impact on this node */ VectorCopy( mid, lightspot ); lightplane = plane; surf = r_worldmodel->surfaces + node->firstsurface; for ( i = 0; i < node->numsurfaces; i++, surf++ ) { if ( surf->flags & ( SURF_DRAWTURB | SURF_DRAWSKY ) ) { continue; /* no lightmaps */ } tex = surf->texinfo; s = DotProduct( mid, tex->vecs [ 0 ] ) + tex->vecs [ 0 ] [ 3 ]; t = DotProduct( mid, tex->vecs [ 1 ] ) + tex->vecs [ 1 ] [ 3 ]; if ( ( s < surf->texturemins [ 0 ] ) || ( t < surf->texturemins [ 1 ] ) ) { continue; } ds = s - surf->texturemins [ 0 ]; dt = t - surf->texturemins [ 1 ]; if ( ( ds > surf->extents [ 0 ] ) || ( dt > surf->extents [ 1 ] ) ) { continue; } if ( !surf->samples ) { return ( 0 ); } ds >>= 4; dt >>= 4; lightmap = surf->samples; VectorCopy( vec3_origin, pointcolor ); if ( lightmap ) { vec3_t scale; lightmap += 3 * ( dt * ( ( surf->extents [ 0 ] >> 4 ) + 1 ) + ds ); for ( maps = 0; maps < MAXLIGHTMAPS && surf->styles [ maps ] != 255; maps++ ) { for ( i = 0; i < 3; i++ ) { scale [ i ] = gl_modulate->value * r_newrefdef.lightstyles [ surf->styles [ maps ] ].rgb [ i ]; } pointcolor [ 0 ] += lightmap [ 0 ] * scale [ 0 ] * ( 1.0 / 255 ); pointcolor [ 1 ] += lightmap [ 1 ] * scale [ 1 ] * ( 1.0 / 255 ); pointcolor [ 2 ] += lightmap [ 2 ] * scale [ 2 ] * ( 1.0 / 255 ); lightmap += 3 * ( ( surf->extents [ 0 ] >> 4 ) + 1 ) * ( ( surf->extents [ 1 ] >> 4 ) + 1 ); } } return ( 1 ); } /* go down back side */ return ( RecursiveLightPoint( node->children [ !side ], mid, end ) ); } void R_LightPoint ( vec3_t p, vec3_t color ) { vec3_t end; float r; int lnum; dlight_t *dl; float light; vec3_t dist; float add; if ( !r_worldmodel->lightdata ) { color [ 0 ] = color [ 1 ] = color [ 2 ] = 1.0; return; } end [ 0 ] = p [ 0 ]; end [ 1 ] = p [ 1 ]; end [ 2 ] = p [ 2 ] - 2048; r = RecursiveLightPoint( r_worldmodel->nodes, p, end ); if ( r == -1 ) { VectorCopy( vec3_origin, color ); } else { VectorCopy( pointcolor, color ); } /* add dynamic lights */ light = 0; dl = r_newrefdef.dlights; for ( lnum = 0; lnum < r_newrefdef.num_dlights; lnum++, dl++ ) { VectorSubtract( currententity->origin, dl->origin, dist ); add = dl->intensity - VectorLength( dist ); add *= ( 1.0 / 256 ); if ( add > 0 ) { VectorMA( color, add, dl->color, color ); } } VectorScale( color, gl_modulate->value, color ); } static float s_blocklights [ 34 * 34 * 3 ]; void R_AddDynamicLights ( msurface_t *surf ) { int lnum; int sd, td; float fdist, frad, fminlight; vec3_t impact, local; int s, t; int i; int smax, tmax; mtexinfo_t *tex; dlight_t *dl; float *pfBL; float fsacc, ftacc; smax = ( surf->extents [ 0 ] >> 4 ) + 1; tmax = ( surf->extents [ 1 ] >> 4 ) + 1; tex = surf->texinfo; for ( lnum = 0; lnum < r_newrefdef.num_dlights; lnum++ ) { if ( !( surf->dlightbits & ( 1 << lnum ) ) ) { continue; /* not lit by this light */ } dl = &r_newrefdef.dlights [ lnum ]; frad = dl->intensity; fdist = DotProduct( dl->origin, surf->plane->normal ) - surf->plane->dist; frad -= fabs( fdist ); /* rad is now the highest intensity on the plane */ fminlight = DLIGHT_CUTOFF; if ( frad < fminlight ) { continue; } fminlight = frad - fminlight; for ( i = 0; i < 3; i++ ) { impact [ i ] = dl->origin [ i ] - surf->plane->normal [ i ] * fdist; } local [ 0 ] = DotProduct( impact, tex->vecs [ 0 ] ) + tex->vecs [ 0 ] [ 3 ] - surf->texturemins [ 0 ]; local [ 1 ] = DotProduct( impact, tex->vecs [ 1 ] ) + tex->vecs [ 1 ] [ 3 ] - surf->texturemins [ 1 ]; pfBL = s_blocklights; for ( t = 0, ftacc = 0; t < tmax; t++, ftacc += 16 ) { td = local [ 1 ] - ftacc; if ( td < 0 ) { td = -td; } for ( s = 0, fsacc = 0; s < smax; s++, fsacc += 16, pfBL += 3 ) { sd = Q_ftol( local [ 0 ] - fsacc ); if ( sd < 0 ) { sd = -sd; } if ( sd > td ) { fdist = sd + ( td >> 1 ); } else { fdist = td + ( sd >> 1 ); } if ( fdist < fminlight ) { pfBL [ 0 ] += ( frad - fdist ) * dl->color [ 0 ]; pfBL [ 1 ] += ( frad - fdist ) * dl->color [ 1 ]; pfBL [ 2 ] += ( frad - fdist ) * dl->color [ 2 ]; } } } } } void R_SetCacheState ( msurface_t *surf ) { int maps; for ( maps = 0; maps < MAXLIGHTMAPS && surf->styles [ maps ] != 255; maps++ ) { surf->cached_light [ maps ] = r_newrefdef.lightstyles [ surf->styles [ maps ] ].white; } } /* * Combine and scale multiple lightmaps into the floating format in blocklights */ void R_BuildLightMap ( msurface_t *surf, byte *dest, int stride ) { int smax, tmax; int r, g, b, a, max; int i, j, size; byte *lightmap; float scale [ 4 ]; int nummaps; float *bl; lightstyle_t *style; int monolightmap; if ( surf->texinfo->flags & ( SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP ) ) { ri.Sys_Error( ERR_DROP, "R_BuildLightMap called for non-lit surface" ); } smax = ( surf->extents [ 0 ] >> 4 ) + 1; tmax = ( surf->extents [ 1 ] >> 4 ) + 1; size = smax * tmax; if ( size > ( sizeof ( s_blocklights ) >> 4 ) ) { ri.Sys_Error( ERR_DROP, "Bad s_blocklights size" ); } /* set to full bright if no light data */ if ( !surf->samples ) { int maps; for ( i = 0; i < size * 3; i++ ) { s_blocklights [ i ] = 255; } for ( maps = 0; maps < MAXLIGHTMAPS && surf->styles [ maps ] != 255; maps++ ) { style = &r_newrefdef.lightstyles [ surf->styles [ maps ] ]; } goto store; } /* count the # of maps */ for ( nummaps = 0; nummaps < MAXLIGHTMAPS && surf->styles [ nummaps ] != 255; nummaps++ ) { } lightmap = surf->samples; /* add all the lightmaps */ if ( nummaps == 1 ) { int maps; for ( maps = 0; maps < MAXLIGHTMAPS && surf->styles [ maps ] != 255; maps++ ) { bl = s_blocklights; for ( i = 0; i < 3; i++ ) { scale [ i ] = gl_modulate->value * r_newrefdef.lightstyles [ surf->styles [ maps ] ].rgb [ i ]; } if ( ( scale [ 0 ] == 1.0F ) && ( scale [ 1 ] == 1.0F ) && ( scale [ 2 ] == 1.0F ) ) { for ( i = 0; i < size; i++, bl += 3 ) { bl [ 0 ] = lightmap [ i * 3 + 0 ]; bl [ 1 ] = lightmap [ i * 3 + 1 ]; bl [ 2 ] = lightmap [ i * 3 + 2 ]; } } else { for ( i = 0; i < size; i++, bl += 3 ) { bl [ 0 ] = lightmap [ i * 3 + 0 ] * scale [ 0 ]; bl [ 1 ] = lightmap [ i * 3 + 1 ] * scale [ 1 ]; bl [ 2 ] = lightmap [ i * 3 + 2 ] * scale [ 2 ]; } } lightmap += size * 3; /* skip to next lightmap */ } } else { int maps; memset( s_blocklights, 0, sizeof ( s_blocklights [ 0 ] ) * size * 3 ); for ( maps = 0; maps < MAXLIGHTMAPS && surf->styles [ maps ] != 255; maps++ ) { bl = s_blocklights; for ( i = 0; i < 3; i++ ) { scale [ i ] = gl_modulate->value * r_newrefdef.lightstyles [ surf->styles [ maps ] ].rgb [ i ]; } if ( ( scale [ 0 ] == 1.0F ) && ( scale [ 1 ] == 1.0F ) && ( scale [ 2 ] == 1.0F ) ) { for ( i = 0; i < size; i++, bl += 3 ) { bl [ 0 ] += lightmap [ i * 3 + 0 ]; bl [ 1 ] += lightmap [ i * 3 + 1 ]; bl [ 2 ] += lightmap [ i * 3 + 2 ]; } } else { for ( i = 0; i < size; i++, bl += 3 ) { bl [ 0 ] += lightmap [ i * 3 + 0 ] * scale [ 0 ]; bl [ 1 ] += lightmap [ i * 3 + 1 ] * scale [ 1 ]; bl [ 2 ] += lightmap [ i * 3 + 2 ] * scale [ 2 ]; } } lightmap += size * 3; /* skip to next lightmap */ } } /* add all the dynamic lights */ if ( surf->dlightframe == r_framecount ) { R_AddDynamicLights( surf ); } store: stride -= ( smax << 2 ); bl = s_blocklights; monolightmap = gl_monolightmap->string [ 0 ]; if ( monolightmap == '0' ) { for ( i = 0; i < tmax; i++, dest += stride ) { for ( j = 0; j < smax; j++ ) { r = Q_ftol( bl [ 0 ] ); g = Q_ftol( bl [ 1 ] ); b = Q_ftol( bl [ 2 ] ); /* catch negative lights */ if ( r < 0 ) { r = 0; } if ( g < 0 ) { g = 0; } if ( b < 0 ) { b = 0; } /* determine the brightest of the three color components */ if ( r > g ) { max = r; } else { max = g; } if ( b > max ) { max = b; } /* alpha is ONLY used for the mono lightmap case. For this reason we set it to the brightest of the color components so that things don't get too dim. */ a = max; /* rescale all the color components if the intensity of the greatest channel exceeds 1.0 */ if ( max > 255 ) { float t = 255.0F / max; r = r * t; g = g * t; b = b * t; a = a * t; } dest [ 0 ] = r; dest [ 1 ] = g; dest [ 2 ] = b; dest [ 3 ] = a; bl += 3; dest += 4; } } } else { for ( i = 0; i < tmax; i++, dest += stride ) { for ( j = 0; j < smax; j++ ) { r = Q_ftol( bl [ 0 ] ); g = Q_ftol( bl [ 1 ] ); b = Q_ftol( bl [ 2 ] ); /* catch negative lights */ if ( r < 0 ) { r = 0; } if ( g < 0 ) { g = 0; } if ( b < 0 ) { b = 0; } /* determine the brightest of the three color components */ if ( r > g ) { max = r; } else { max = g; } if ( b > max ) { max = b; } /* alpha is ONLY used for the mono lightmap case. For this reason we set it to the brightest of the color components so that things don't get too dim. */ a = max; /* rescale all the color components if the intensity of the greatest channel exceeds 1.0 */ if ( max > 255 ) { float t = 255.0F / max; r = r * t; g = g * t; b = b * t; a = a * t; } /* So if we are doing alpha lightmaps we need to set the R, G, and B components to 0 and we need to set alpha to 1-alpha. */ switch ( monolightmap ) { case 'L': case 'I': r = a; g = b = 0; break; case 'C': /* try faking colored lighting */ a = 255 - ( ( r + g + b ) / 3 ); r *= a / 255.0; g *= a / 255.0; b *= a / 255.0; break; case 'A': default: r = g = b = 0; a = 255 - a; break; } dest [ 0 ] = r; dest [ 1 ] = g; dest [ 2 ] = b; dest [ 3 ] = a; bl += 3; dest += 4; } } } }