Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

light.c (51041B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // light.c
     23 
     24 #include "light.h"
     25 #ifdef _WIN32
     26 #ifdef _TTIMOBUILD
     27 #include "pakstuff.h"
     28 #else
     29 #include "../libs/pakstuff.h"
     30 #endif
     31 #endif
     32 
     33 
     34 #define	EXTRASCALE	2
     35 
     36 typedef struct {
     37 	float		plane[4];
     38 	vec3_t		origin;
     39 	vec3_t		vectors[2];
     40 	shaderInfo_t	*si;
     41 } filter_t;
     42 
     43 #define	MAX_FILTERS	1024
     44 filter_t	filters[MAX_FILTERS];
     45 int			numFilters;
     46 
     47 extern char	source[1024];
     48 
     49 qboolean	notrace;
     50 qboolean	patchshadows;
     51 qboolean	dump;
     52 qboolean	extra;
     53 qboolean	extraWide;
     54 qboolean	lightmapBorder;
     55 
     56 qboolean	noSurfaces;
     57 
     58 int			samplesize = 16;		//sample size in units
     59 int			novertexlighting = 0;
     60 int			nogridlighting = 0;
     61 
     62 // for run time tweaking of all area sources in the level
     63 float		areaScale =	0.25;
     64 
     65 // for run time tweaking of all point sources in the level
     66 float		pointScale = 7500;
     67 
     68 qboolean	exactPointToPolygon = qtrue;
     69 
     70 float		formFactorValueScale = 3;
     71 
     72 float		linearScale = 1.0 / 8000;
     73 
     74 light_t		*lights;
     75 int			numPointLights;
     76 int			numAreaLights;
     77 
     78 FILE		*dumpFile;
     79 
     80 int			c_visible, c_occluded;
     81 
     82 //int			defaultLightSubdivide = 128;		// vary by surface size?
     83 int			defaultLightSubdivide = 999;		// vary by surface size?
     84 
     85 vec3_t		ambientColor;
     86 
     87 vec3_t		surfaceOrigin[ MAX_MAP_DRAW_SURFS ];
     88 int			entitySurface[ MAX_MAP_DRAW_SURFS ];
     89 
     90 // 7,9,11 normalized to avoid being nearly coplanar with common faces
     91 //vec3_t		sunDirection = { 0.441835, 0.56807, 0.694313 };
     92 //vec3_t		sunDirection = { 0.45, 0, 0.9 };
     93 //vec3_t		sunDirection = { 0, 0, 1 };
     94 
     95 // these are usually overrided by shader values
     96 vec3_t		sunDirection = { 0.45, 0.3, 0.9 };
     97 vec3_t		sunLight = { 100, 100, 50 };
     98 
     99 
    100 
    101 typedef struct {
    102 	dbrush_t	*b;
    103 	vec3_t		bounds[2];
    104 } skyBrush_t;
    105 
    106 int			numSkyBrushes;
    107 skyBrush_t	skyBrushes[MAX_MAP_BRUSHES];
    108 
    109 
    110 /*
    111 
    112 the corners of a patch mesh will always be exactly at lightmap samples.
    113 The dimensions of the lightmap will be equal to the average length of the control
    114 mesh in each dimension divided by 2.
    115 The lightmap sample points should correspond to the chosen subdivision points.
    116 
    117 */
    118 
    119 /*
    120 ===============================================================
    121 
    122 SURFACE LOADING
    123 
    124 ===============================================================
    125 */
    126 
    127 #define	MAX_FACE_POINTS		128
    128 
    129 /*
    130 ===============
    131 SubdivideAreaLight
    132 
    133 Subdivide area lights that are very large
    134 A light that is subdivided will never backsplash, avoiding weird pools of light near edges
    135 ===============
    136 */
    137 void SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal, 
    138 						float areaSubdivide, qboolean backsplash ) {
    139 	float			area, value, intensity;
    140 	light_t			*dl, *dl2;
    141 	vec3_t			mins, maxs;
    142 	int				axis;
    143 	winding_t		*front, *back;
    144 	vec3_t			planeNormal;
    145 	float			planeDist;
    146 
    147 	if ( !w ) {
    148 		return;
    149 	}
    150 
    151 	WindingBounds( w, mins, maxs );
    152 
    153 	// check for subdivision
    154 	for ( axis = 0 ; axis < 3 ; axis++ ) {
    155 		if ( maxs[axis] - mins[axis] > areaSubdivide ) {
    156 			VectorClear( planeNormal );
    157 			planeNormal[axis] = 1;
    158 			planeDist = ( maxs[axis] + mins[axis] ) * 0.5;
    159 			ClipWindingEpsilon ( w, planeNormal, planeDist, ON_EPSILON, &front, &back );
    160 			SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse );
    161 			SubdivideAreaLight( ls, back, normal, areaSubdivide, qfalse );
    162 			FreeWinding( w );
    163 			return;
    164 		}
    165 	}
    166 
    167 	// create a light from this
    168 	area = WindingArea (w);
    169 	if ( area <= 0 || area > 20000000 ) {
    170 		return;
    171 	}
    172 
    173 	numAreaLights++;
    174 	dl = malloc(sizeof(*dl));
    175 	memset (dl, 0, sizeof(*dl));
    176 	dl->next = lights;
    177 	lights = dl;
    178 	dl->type = emit_area;
    179 
    180 	WindingCenter( w, dl->origin );
    181 	dl->w = w;
    182 	VectorCopy ( normal, dl->normal);
    183 	dl->dist = DotProduct( dl->origin, normal );
    184 
    185 	value = ls->value;
    186 	intensity = value * area * areaScale;
    187 	VectorAdd( dl->origin, dl->normal, dl->origin );
    188 
    189 	VectorCopy( ls->color, dl->color );
    190 
    191 	dl->photons = intensity;
    192 
    193 	// emitColor is irrespective of the area
    194 	VectorScale( ls->color, value*formFactorValueScale*areaScale, dl->emitColor );
    195 
    196 	dl->si = ls;
    197 
    198 	if ( ls->contents & CONTENTS_FOG ) {
    199 		dl->twosided = qtrue;
    200 	}
    201 
    202 	// optionally create a point backsplash light
    203 	if ( backsplash && ls->backsplashFraction > 0 ) {
    204 		dl2 = malloc(sizeof(*dl));
    205 		memset (dl2, 0, sizeof(*dl2));
    206 		dl2->next = lights;
    207 		lights = dl2;
    208 		dl2->type = emit_point;
    209 
    210 		VectorMA( dl->origin, ls->backsplashDistance, normal, dl2->origin );
    211 
    212 		VectorCopy( ls->color, dl2->color );
    213 
    214 		dl2->photons = dl->photons * ls->backsplashFraction;
    215 		dl2->si = ls;
    216 	}
    217 }
    218 
    219 
    220 /*
    221 ===============
    222 CountLightmaps
    223 ===============
    224 */
    225 void CountLightmaps( void ) {
    226 	int			count;
    227 	int			i;
    228 	dsurface_t	*ds;
    229 
    230 	qprintf ("--- CountLightmaps ---\n");
    231 	count = 0;
    232 	for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
    233 		// see if this surface is light emiting
    234 		ds = &drawSurfaces[i];
    235 		if ( ds->lightmapNum > count ) {
    236 			count = ds->lightmapNum;
    237 		}
    238 	}
    239 
    240 	count++;
    241 	numLightBytes = count * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3;
    242 	if ( numLightBytes > MAX_MAP_LIGHTING ) {
    243 		Error("MAX_MAP_LIGHTING exceeded");
    244 	}
    245 
    246 	qprintf( "%5i drawSurfaces\n", numDrawSurfaces );
    247 	qprintf( "%5i lightmaps\n", count );
    248 }
    249 
    250 /*
    251 ===============
    252 CreateSurfaceLights
    253 
    254 This creates area lights
    255 ===============
    256 */
    257 void CreateSurfaceLights( void ) {
    258 	int				i, j, side;
    259 	dsurface_t		*ds;
    260 	shaderInfo_t	*ls;
    261 	winding_t		*w;
    262 	cFacet_t		*f;
    263 	light_t			*dl;
    264 	vec3_t			origin;
    265 	drawVert_t		*dv;
    266 	int				c_lightSurfaces;
    267 	float			lightSubdivide;
    268 	vec3_t			normal;
    269 
    270 	qprintf ("--- CreateSurfaceLights ---\n");
    271 	c_lightSurfaces = 0;
    272 
    273 	for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
    274 		// see if this surface is light emiting
    275 		ds = &drawSurfaces[i];
    276 
    277 		ls = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
    278 		if ( ls->value == 0 ) {
    279 			continue;
    280 		}
    281 
    282 		// determine how much we need to chop up the surface
    283 		if ( ls->lightSubdivide ) {
    284 			lightSubdivide = ls->lightSubdivide;
    285 		} else {
    286 			lightSubdivide = defaultLightSubdivide;
    287 		}
    288 
    289 		c_lightSurfaces++;
    290 
    291 		// an autosprite shader will become
    292 		// a point light instead of an area light
    293 		if ( ls->autosprite ) {
    294 			// autosprite geometry should only have four vertexes
    295 			if ( surfaceTest[i] ) {
    296 				// curve or misc_model
    297 				f = surfaceTest[i]->facets;
    298 				if ( surfaceTest[i]->numFacets != 1 || f->numBoundaries != 4 ) {
    299 					_printf( "WARNING: surface at (%i %i %i) has autosprite shader but isn't a quad\n",
    300 						(int)f->points[0], (int)f->points[1], (int)f->points[2] );
    301 				}
    302 				VectorAdd( f->points[0], f->points[1], origin );
    303 				VectorAdd( f->points[2], origin, origin );
    304 				VectorAdd( f->points[3], origin, origin );
    305 				VectorScale( origin, 0.25, origin );
    306 			} else {
    307 				// normal polygon
    308 				dv = &drawVerts[ ds->firstVert ];
    309 				if ( ds->numVerts != 4 ) {
    310 					_printf( "WARNING: surface at (%i %i %i) has autosprite shader but %i verts\n",
    311 						(int)dv->xyz[0], (int)dv->xyz[1], (int)dv->xyz[2] );
    312 					continue;
    313 				}
    314 
    315 				VectorAdd( dv[0].xyz, dv[1].xyz, origin );
    316 				VectorAdd( dv[2].xyz, origin, origin );
    317 				VectorAdd( dv[3].xyz, origin, origin );
    318 				VectorScale( origin, 0.25, origin );
    319 			}
    320 
    321 
    322 			numPointLights++;
    323 			dl = malloc(sizeof(*dl));
    324 			memset (dl, 0, sizeof(*dl));
    325 			dl->next = lights;
    326 			lights = dl;
    327 
    328 			VectorCopy( origin, dl->origin );
    329 			VectorCopy( ls->color, dl->color );
    330 			dl->photons = ls->value * pointScale;
    331 			dl->type = emit_point;
    332 			continue;
    333 		}
    334 
    335 		// possibly create for both sides of the polygon
    336 		for ( side = 0 ; side <= ls->twoSided ; side++ ) {
    337 			// create area lights
    338 			if ( surfaceTest[i] ) {
    339 				// curve or misc_model
    340 				for ( j = 0 ; j < surfaceTest[i]->numFacets ; j++ ) {
    341 					f = surfaceTest[i]->facets + j;
    342 					w = AllocWinding( f->numBoundaries );
    343 					w->numpoints = f->numBoundaries;
    344 					memcpy( w->p, f->points, f->numBoundaries * 12 );
    345 
    346 					VectorCopy( f->surface, normal );
    347 					if ( side ) {
    348 						winding_t	*t;
    349 
    350 						t = w;
    351 						w = ReverseWinding( t );
    352 						FreeWinding( t );
    353 						VectorSubtract( vec3_origin, normal, normal );
    354 					}
    355 					SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
    356 				}
    357 			} else {
    358 				// normal polygon
    359 
    360 				w = AllocWinding( ds->numVerts );
    361 				w->numpoints = ds->numVerts;
    362 				for ( j = 0 ; j < ds->numVerts ; j++ ) {
    363 					VectorCopy( drawVerts[ds->firstVert+j].xyz, w->p[j] );
    364 				}
    365 				VectorCopy( ds->lightmapVecs[2], normal );
    366 				if ( side ) {
    367 					winding_t	*t;
    368 
    369 					t = w;
    370 					w = ReverseWinding( t );
    371 					FreeWinding( t );
    372 					VectorSubtract( vec3_origin, normal, normal );
    373 				}
    374 				SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
    375 			}
    376 		}
    377 	}
    378 
    379 	_printf( "%5i light emitting surfaces\n", c_lightSurfaces );
    380 }
    381 
    382 
    383 
    384 /*
    385 ================
    386 FindSkyBrushes
    387 ================
    388 */
    389 void FindSkyBrushes( void ) {
    390 	int				i, j;
    391 	dbrush_t		*b;
    392 	skyBrush_t		*sb;
    393 	shaderInfo_t	*si;
    394 	dbrushside_t	*s;
    395 
    396 	// find the brushes
    397 	for ( i = 0 ; i < numbrushes ; i++ ) {
    398 		b = &dbrushes[i];
    399 		for ( j = 0 ; j < b->numSides ; j++ ) {
    400 			s = &dbrushsides[ b->firstSide + j ];
    401 			if ( dshaders[ s->shaderNum ].surfaceFlags & SURF_SKY ) {
    402 				sb = &skyBrushes[ numSkyBrushes ];
    403 				sb->b = b;
    404 				sb->bounds[0][0] = -dplanes[ dbrushsides[ b->firstSide + 0 ].planeNum ].dist - 1;
    405 				sb->bounds[1][0] = dplanes[ dbrushsides[ b->firstSide + 1 ].planeNum ].dist + 1;
    406 				sb->bounds[0][1] = -dplanes[ dbrushsides[ b->firstSide + 2 ].planeNum ].dist - 1;
    407 				sb->bounds[1][1] = dplanes[ dbrushsides[ b->firstSide + 3 ].planeNum ].dist + 1;
    408 				sb->bounds[0][2] = -dplanes[ dbrushsides[ b->firstSide + 4 ].planeNum ].dist - 1;
    409 				sb->bounds[1][2] = dplanes[ dbrushsides[ b->firstSide + 5 ].planeNum ].dist + 1;
    410 				numSkyBrushes++;
    411 				break;
    412 			}
    413 		}
    414 	}
    415 
    416 	// default
    417 	VectorNormalize( sunDirection, sunDirection );
    418 
    419 	// find the sky shader
    420 	for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
    421 		si = ShaderInfoForShader( dshaders[ drawSurfaces[i].shaderNum ].shader );
    422 		if ( si->surfaceFlags & SURF_SKY ) {
    423 			VectorCopy( si->sunLight, sunLight );
    424 			VectorCopy( si->sunDirection, sunDirection );
    425 			break;
    426 		}
    427 	}
    428 }
    429 
    430 /*
    431 =================================================================
    432 
    433   LIGHT SETUP
    434 
    435 =================================================================
    436 */
    437 
    438 /*
    439 ==================
    440 FindTargetEntity
    441 ==================
    442 */
    443 entity_t *FindTargetEntity( const char *target ) {
    444 	int			i;
    445 	const char	*n;
    446 
    447 	for ( i = 0 ; i < num_entities ; i++ ) {
    448 		n = ValueForKey (&entities[i], "targetname");
    449 		if ( !strcmp (n, target) ) {
    450 			return &entities[i];
    451 		}
    452 	}
    453 
    454 	return NULL;
    455 }
    456 
    457 
    458 
    459 /*
    460 =============
    461 CreateEntityLights
    462 =============
    463 */
    464 void CreateEntityLights (void)
    465 {
    466 	int		i;
    467 	light_t	*dl;
    468 	entity_t	*e, *e2;
    469 	const char	*name;
    470 	const char	*target;
    471 	vec3_t	dest;
    472 	const char	*_color;
    473 	float	intensity;
    474 	int		spawnflags;
    475 
    476 	//
    477 	// entities
    478 	//
    479 	for ( i = 0 ; i < num_entities ; i++ ) {
    480 		e = &entities[i];
    481 		name = ValueForKey (e, "classname");
    482 		if (strncmp (name, "light", 5))
    483 			continue;
    484 
    485 		numPointLights++;
    486 		dl = malloc(sizeof(*dl));
    487 		memset (dl, 0, sizeof(*dl));
    488 		dl->next = lights;
    489 		lights = dl;
    490 
    491 		spawnflags = FloatForKey (e, "spawnflags");
    492 		if ( spawnflags & 1 ) {
    493 			dl->linearLight = qtrue;
    494 		}
    495 
    496 		GetVectorForKey (e, "origin", dl->origin);
    497 		dl->style = FloatForKey (e, "_style");
    498 		if (!dl->style)
    499 			dl->style = FloatForKey (e, "style");
    500 		if (dl->style < 0)
    501 			dl->style = 0;
    502 
    503 		intensity = FloatForKey (e, "light");
    504 		if (!intensity)
    505 			intensity = FloatForKey (e, "_light");
    506 		if (!intensity)
    507 			intensity = 300;
    508 		_color = ValueForKey (e, "_color");
    509 		if (_color && _color[0])
    510 		{
    511 			sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]);
    512 			ColorNormalize (dl->color, dl->color);
    513 		}
    514 		else
    515 			dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
    516 
    517 		intensity = intensity * pointScale;
    518 		dl->photons = intensity;
    519 
    520 		dl->type = emit_point;
    521 
    522 		// lights with a target will be spotlights
    523 		target = ValueForKey (e, "target");
    524 
    525 		if ( target[0] ) {
    526 			float	radius;
    527 			float	dist;
    528 
    529 			e2 = FindTargetEntity (target);
    530 			if (!e2) {
    531 				_printf ("WARNING: light at (%i %i %i) has missing target\n",
    532 				(int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
    533 			} else {
    534 				GetVectorForKey (e2, "origin", dest);
    535 				VectorSubtract (dest, dl->origin, dl->normal);
    536 				dist = VectorNormalize (dl->normal, dl->normal);
    537 				radius = FloatForKey (e, "radius");
    538 				if ( !radius ) {
    539 					radius = 64;
    540 				}
    541 				if ( !dist ) {
    542 					dist = 64;
    543 				}
    544 				dl->radiusByDist = (radius + 16) / dist;
    545 				dl->type = emit_spotlight;
    546 			}
    547 		}
    548 	}
    549 }
    550 
    551 //=================================================================
    552 
    553 /*
    554 ================
    555 SetEntityOrigins
    556 
    557 Find the offset values for inline models
    558 ================
    559 */
    560 void SetEntityOrigins( void ) {
    561 	int			i, j;
    562 	entity_t	*e;
    563 	vec3_t		origin;
    564 	const char	*key;
    565 	int			modelnum;
    566 	dmodel_t	*dm;
    567 
    568 	for ( i=0 ; i < num_entities ; i++ ) {
    569 		e = &entities[i];
    570 		key = ValueForKey (e, "model");
    571 		if ( key[0] != '*' ) {
    572 			continue;
    573 		}
    574 		modelnum = atoi( key + 1 );
    575 		dm = &dmodels[ modelnum ];
    576 
    577 		// set entity surface to true for all surfaces for this model
    578 		for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
    579 			entitySurface[ dm->firstSurface + j ] = qtrue;
    580 		}
    581 
    582 		key = ValueForKey (e, "origin");
    583 		if ( !key[0] ) {
    584 			continue;
    585 		}
    586 		GetVectorForKey ( e, "origin", origin );
    587 
    588 		// set origin for all surfaces for this model
    589 		for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
    590 			VectorCopy( origin, surfaceOrigin[ dm->firstSurface + j ] );
    591 		}
    592 	}
    593 }
    594 
    595 
    596 /*
    597 =================================================================
    598 
    599 
    600 =================================================================
    601 */
    602 
    603 #define	MAX_POINTS_ON_WINDINGS	64
    604 
    605 /*
    606 ================
    607 PointToPolygonFormFactor
    608 ================
    609 */
    610 float	PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w ) {
    611 	vec3_t		triVector, triNormal;
    612 	int			i, j;
    613 	vec3_t		dirs[MAX_POINTS_ON_WINDING];
    614 	float		total;
    615 	float		dot, angle, facing;
    616 
    617 	for ( i = 0 ; i < w->numpoints ; i++ ) {
    618 		VectorSubtract( w->p[i], point, dirs[i] );
    619 		VectorNormalize( dirs[i], dirs[i] );
    620 	}
    621 
    622 	// duplicate first vertex to avoid mod operation
    623 	VectorCopy( dirs[0], dirs[i] );
    624 
    625 	total = 0;
    626 	for ( i = 0 ; i < w->numpoints ; i++ ) {
    627 		j = i+1;
    628 		dot = DotProduct( dirs[i], dirs[j] );
    629 
    630 		// roundoff can cause slight creep, which gives an IND from acos
    631 		if ( dot > 1.0 ) {
    632 			dot = 1.0;
    633 		} else if ( dot < -1.0 ) {
    634 			dot = -1.0;
    635 		}
    636 		
    637 		angle = acos( dot );
    638 		CrossProduct( dirs[i], dirs[j], triVector );
    639 		if ( VectorNormalize( triVector, triNormal ) < 0.0001 ) {
    640 			continue;
    641 		}
    642 		facing = DotProduct( normal, triNormal );
    643 		total += facing * angle;
    644 
    645 		if ( total > 6.3 || total < -6.3 ) {
    646 			static qboolean printed;
    647 
    648 			if ( !printed ) {
    649 				printed = qtrue;
    650 				_printf( "WARNING: bad PointToPolygonFormFactor: %f at %1.1f %1.1f %1.1f from %1.1f %1.1f %1.1f\n", total,
    651 					w->p[i][0], w->p[i][1], w->p[i][2], point[0], point[1], point[2]);
    652 			}
    653 			return 0;
    654 		}
    655 
    656 	}
    657 
    658 	total /= 2*3.141592657;		// now in the range of 0 to 1 over the entire incoming hemisphere
    659 
    660 	return total;
    661 }
    662 
    663 
    664 /*
    665 ================
    666 FilterTrace
    667 
    668 Returns 0 to 1.0 filter fractions for the given trace
    669 ================
    670 */
    671 void	FilterTrace( const vec3_t start, const vec3_t end, vec3_t filter ) {
    672 	float		d1, d2;
    673 	filter_t	*f;
    674 	int			filterNum;
    675 	vec3_t		point;
    676 	float		frac;
    677 	int			i;
    678 	float		s, t;
    679 	int			u, v;
    680 	int			x, y;
    681 	byte		*pixel;
    682 	float		radius;
    683 	float		len;
    684 	vec3_t		total;
    685 
    686 	filter[0] = 1.0;
    687 	filter[1] = 1.0;
    688 	filter[2] = 1.0;
    689 
    690 	for ( filterNum = 0 ; filterNum < numFilters ; filterNum++ ) {
    691 		f = &filters[ filterNum ];
    692 
    693 		// see if the plane is crossed
    694 		d1 = DotProduct( start, f->plane ) - f->plane[3];
    695 		d2 = DotProduct( end, f->plane ) - f->plane[3];
    696 
    697 		if ( ( d1 < 0 ) == ( d2 < 0 ) ) {
    698 			continue;
    699 		}
    700 
    701 		// calculate the crossing point
    702 		frac = d1 / ( d1 - d2 );
    703 
    704 		for ( i = 0 ; i < 3 ; i++ ) {
    705 			point[i] = start[i] + frac * ( end[i] - start[i] );
    706 		}
    707 
    708 		VectorSubtract( point, f->origin, point );
    709 
    710 		s = DotProduct( point, f->vectors[0] );
    711 		t = 1.0 - DotProduct( point, f->vectors[1] );
    712 		if ( s < 0 || s >= 1.0 || t < 0 || t >= 1.0 ) {
    713 			continue;
    714 		}
    715 
    716 		// decide the filter size
    717 		radius = 10 * frac;
    718 		len = VectorLength( f->vectors[0] );
    719 		if ( !len ) {
    720 			continue;
    721 		}
    722 		radius = radius * len * f->si->width;
    723 
    724 		// look up the filter, taking multiple samples
    725 		VectorClear( total );
    726 		for ( u = -1 ; u <= 1 ; u++ ) {
    727 			for ( v = -1 ; v <=1 ; v++ ) {
    728 				x = s * f->si->width + u * radius;
    729 				if ( x < 0 ) {
    730 					x = 0;
    731 				}
    732 				if ( x >= f->si->width ) {
    733 					x = f->si->width - 1;
    734 				}
    735 				y = t * f->si->height + v * radius;
    736 				if ( y < 0 ) {
    737 					y = 0;
    738 				}
    739 				if ( y >= f->si->height ) {
    740 					y = f->si->height - 1;
    741 				}
    742 
    743 				pixel = f->si->pixels + ( y * f->si->width + x ) * 4;
    744 				total[0] += pixel[0];
    745 				total[1] += pixel[1];
    746 				total[2] += pixel[2];
    747 			}
    748 		}
    749 
    750 		filter[0] *= total[0]/(255.0*9);
    751 		filter[1] *= total[1]/(255.0*9);
    752 		filter[2] *= total[2]/(255.0*9);
    753 	}
    754 
    755 }
    756 
    757 /*
    758 ================
    759 SunToPoint
    760 
    761 Returns an amount of light to add at the point
    762 ================
    763 */
    764 int		c_sunHit, c_sunMiss;
    765 void SunToPoint( const vec3_t origin, traceWork_t *tw, vec3_t addLight ) {
    766 	int			i;
    767 	trace_t		trace;
    768 	skyBrush_t	*b;
    769 	vec3_t		end;
    770 
    771 	if ( !numSkyBrushes ) {
    772 		VectorClear( addLight );
    773 		return;
    774 	}
    775 
    776 	VectorMA( origin, MAX_WORLD_COORD * 2, sunDirection, end );
    777 
    778 	TraceLine( origin, end, &trace, qtrue, tw );
    779 
    780 	// see if trace.hit is inside a sky brush
    781 	for ( i = 0 ; i < numSkyBrushes ; i++) {
    782 		b = &skyBrushes[ i ];
    783 
    784 		// this assumes that sky brushes are axial...
    785 		if (   trace.hit[0] < b->bounds[0][0] 
    786 			|| trace.hit[0] > b->bounds[1][0]
    787 			|| trace.hit[1] < b->bounds[0][1]
    788 			|| trace.hit[1] > b->bounds[1][1]
    789 			|| trace.hit[2] < b->bounds[0][2]
    790 			|| trace.hit[2] > b->bounds[1][2] ) {
    791 			continue;
    792 		}
    793 
    794 
    795 		// trace again to get intermediate filters
    796 		TraceLine( origin, trace.hit, &trace, qtrue, tw );
    797 
    798 		// we hit the sky, so add sunlight
    799 		if ( numthreads == 1 ) {
    800 			c_sunHit++;
    801 		}
    802 		addLight[0] = trace.filter[0] * sunLight[0];
    803 		addLight[1] = trace.filter[1] * sunLight[1];
    804 		addLight[2] = trace.filter[2] * sunLight[2];
    805 
    806 		return;
    807 	}
    808 
    809 	if ( numthreads == 1 ) {
    810 		c_sunMiss++;
    811 	}
    812 
    813 	VectorClear( addLight );
    814 }
    815 
    816 /*
    817 ================
    818 SunToPlane
    819 ================
    820 */
    821 void SunToPlane( const vec3_t origin, const vec3_t normal, vec3_t color, traceWork_t *tw ) {
    822 	float		angle;
    823 	vec3_t		sunColor;
    824 
    825 	if ( !numSkyBrushes ) {
    826 		return;
    827 	}
    828 
    829 	angle = DotProduct( normal, sunDirection );
    830 	if ( angle <= 0 ) {
    831 		return;		// facing away
    832 	}
    833 
    834 	SunToPoint( origin, tw, sunColor );
    835 	VectorMA( color, angle, sunColor, color );
    836 }
    837 
    838 /*
    839 ================
    840 LightingAtSample
    841 ================
    842 */
    843 void LightingAtSample( vec3_t origin, vec3_t normal, vec3_t color, 
    844 					  qboolean testOcclusion, qboolean forceSunLight, traceWork_t *tw ) {
    845 	light_t		*light;
    846 	trace_t		trace;
    847 	float		angle;
    848 	float		add;
    849 	float		dist;
    850 	vec3_t		dir;
    851 
    852 	VectorCopy( ambientColor, color );
    853 
    854 	// trace to all the lights
    855 	for ( light = lights ; light ; light = light->next ) {
    856 
    857 		//MrE: if the light is behind the surface
    858 		if ( DotProduct(light->origin, normal) - DotProduct(normal, origin) < 0 )
    859 			continue;
    860 		// testing exact PTPFF
    861 		if ( exactPointToPolygon && light->type == emit_area ) {
    862 			float		factor;
    863 			float		d;
    864 			vec3_t		pushedOrigin;
    865 
    866 			// see if the point is behind the light
    867 			d = DotProduct( origin, light->normal ) - light->dist;
    868 			if ( !light->twosided ) {
    869 				if ( d < -1 ) {
    870 					continue;		// point is behind light
    871 				}
    872 			}
    873 
    874 			// test occlusion and find light filters
    875 			// clip the line, tracing from the surface towards the light
    876 			if ( !notrace && testOcclusion ) {
    877 				TraceLine( origin, light->origin, &trace, qfalse, tw );
    878 
    879 				// other light rays must not hit anything
    880 				if ( trace.passSolid ) {
    881 					continue;
    882 				}
    883 			} else {
    884 				trace.filter[0] = 1.0;
    885 				trace.filter[1] = 1.0;
    886 				trace.filter[2] = 1.0;
    887 			}
    888 
    889 			// nudge the point so that it is clearly forward of the light
    890 			// so that surfaces meeting a light emiter don't get black edges
    891 			if ( d > -8 && d < 8 ) {
    892 				VectorMA( origin, (8-d), light->normal, pushedOrigin );	
    893 			} else {
    894 				VectorCopy( origin, pushedOrigin );
    895 			}
    896 
    897 			// calculate the contribution
    898 			factor = PointToPolygonFormFactor( pushedOrigin, normal, light->w );
    899 			if ( factor <= 0 ) {
    900 				if ( light->twosided ) {
    901 					factor = -factor;
    902 				} else {
    903 					continue;
    904 				}
    905 			}
    906 			color[0] += factor * light->emitColor[0] * trace.filter[0];
    907 			color[1] += factor * light->emitColor[1] * trace.filter[1];
    908 			color[2] += factor * light->emitColor[2] * trace.filter[2];
    909 
    910 			continue;
    911 		}
    912 
    913 		// calculate the amount of light at this sample
    914 		if ( light->type == emit_point ) {
    915 			VectorSubtract( light->origin, origin, dir );
    916 			dist = VectorNormalize( dir, dir );
    917 			// clamp the distance to prevent super hot spots
    918 			if ( dist < 16 ) {
    919 				dist = 16;
    920 			}
    921 			angle = DotProduct( normal, dir );
    922 			if ( light->linearLight ) {
    923 				add = angle * light->photons * linearScale - dist;
    924 				if ( add < 0 ) {
    925 					add = 0;
    926 				}
    927 			} else {
    928 				add = light->photons / ( dist * dist ) * angle;
    929 			}
    930 		} else if ( light->type == emit_spotlight ) {
    931 			float	distByNormal;
    932 			vec3_t	pointAtDist;
    933 			float	radiusAtDist;
    934 			float	sampleRadius;
    935 			vec3_t	distToSample;
    936 			float	coneScale;
    937 
    938 			VectorSubtract( light->origin, origin, dir );
    939 
    940 			distByNormal = -DotProduct( dir, light->normal );
    941 			if ( distByNormal < 0 ) {
    942 				continue;
    943 			}
    944 			VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
    945 			radiusAtDist = light->radiusByDist * distByNormal;
    946 
    947 			VectorSubtract( origin, pointAtDist, distToSample );
    948 			sampleRadius = VectorLength( distToSample );
    949 
    950 			if ( sampleRadius >= radiusAtDist ) {
    951 				continue;		// outside the cone
    952 			}
    953 			if ( sampleRadius <= radiusAtDist - 32 ) {
    954 				coneScale = 1.0;	// fully inside
    955 			} else {
    956 				coneScale = ( radiusAtDist - sampleRadius ) / 32.0;
    957 			}
    958 			
    959 			dist = VectorNormalize( dir, dir );
    960 			// clamp the distance to prevent super hot spots
    961 			if ( dist < 16 ) {
    962 				dist = 16;
    963 			}
    964 			angle = DotProduct( normal, dir );
    965 			add = light->photons / ( dist * dist ) * angle * coneScale;
    966 
    967 		} else if ( light->type == emit_area ) {
    968 			VectorSubtract( light->origin, origin, dir );
    969 			dist = VectorNormalize( dir, dir );
    970 			// clamp the distance to prevent super hot spots
    971 			if ( dist < 16 ) {
    972 				dist = 16;
    973 			}
    974 			angle = DotProduct( normal, dir );
    975 			if ( angle <= 0 ) {
    976 				continue;
    977 			}
    978 			angle *= -DotProduct( light->normal, dir );
    979 			if ( angle <= 0 ) {
    980 				continue;
    981 			}
    982 
    983 			if ( light->linearLight ) {
    984 				add = angle * light->photons * linearScale - dist;
    985 				if ( add < 0 ) {
    986 					add = 0;
    987 				}
    988 			} else {
    989 				add = light->photons / ( dist * dist ) * angle;
    990 			}
    991 		}
    992 
    993 		if ( add <= 1.0 ) {
    994 			continue;
    995 		}
    996 
    997 		// clip the line, tracing from the surface towards the light
    998 		if ( !notrace && testOcclusion ) {
    999 			TraceLine( origin, light->origin, &trace, qfalse, tw );
   1000 
   1001 			// other light rays must not hit anything
   1002 			if ( trace.passSolid ) {
   1003 				continue;
   1004 			}
   1005 		} else {
   1006 			trace.filter[0] = 1;
   1007 			trace.filter[1] = 1;
   1008 			trace.filter[2] = 1;
   1009 		}
   1010 		
   1011 		// add the result
   1012 		color[0] += add * light->color[0] * trace.filter[0];
   1013 		color[1] += add * light->color[1] * trace.filter[1];
   1014 		color[2] += add * light->color[2] * trace.filter[2];
   1015 	}
   1016 
   1017 	//
   1018 	// trace directly to the sun
   1019 	//
   1020 	if ( testOcclusion || forceSunLight ) {
   1021 		SunToPlane( origin, normal, color, tw );
   1022 	}
   1023 }
   1024 
   1025 /*
   1026 =============
   1027 PrintOccluded
   1028 
   1029 For debugging
   1030 =============
   1031 */
   1032 void PrintOccluded( byte occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE], 
   1033 				   int width, int height ) {
   1034 	int	i, j;
   1035 
   1036 	_printf( "\n" );
   1037 
   1038 	for ( i = 0 ; i < height ; i++ ) {
   1039 		for ( j = 0 ; j < width ; j++ ) {
   1040 			_printf("%i", (int)occluded[j][i] );
   1041 		}
   1042 		_printf( "\n" );
   1043 	}
   1044 }
   1045 
   1046 
   1047 /*
   1048 =============
   1049 VertexLighting
   1050 
   1051 Vertex lighting will completely ignore occlusion, because
   1052 shadows would not be resolvable anyway.
   1053 =============
   1054 */
   1055 void VertexLighting( dsurface_t *ds, qboolean testOcclusion, qboolean forceSunLight, float scale, traceWork_t *tw ) {
   1056 	int			i, j;
   1057 	drawVert_t	*dv;
   1058 	vec3_t		sample, normal;
   1059 	float		max;
   1060 
   1061 	VectorCopy( ds->lightmapVecs[2], normal );
   1062 
   1063 	// generate vertex lighting
   1064 	for ( i = 0 ; i < ds->numVerts ; i++ ) {
   1065 		dv = &drawVerts[ ds->firstVert + i ];
   1066 
   1067 		if ( ds->patchWidth ) {
   1068 			LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
   1069 		}
   1070 		else if (ds->surfaceType == MST_TRIANGLE_SOUP) {
   1071 			LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
   1072 		}
   1073 		else {
   1074 			LightingAtSample( dv->xyz, normal, sample, testOcclusion, forceSunLight, tw );
   1075 		}
   1076 
   1077 		if (scale >= 0)
   1078 			VectorScale(sample, scale, sample);
   1079 		// clamp with color normalization
   1080 		max = sample[0];
   1081 		if ( sample[1] > max ) {
   1082 			max = sample[1];
   1083 		}
   1084 		if ( sample[2] > max ) {
   1085 			max = sample[2];
   1086 		}
   1087 		if ( max > 255 ) {
   1088 			VectorScale( sample, 255/max, sample );
   1089 		}
   1090 
   1091 		// save the sample
   1092 		for ( j = 0 ; j < 3 ; j++ ) {
   1093 			if ( sample[j] > 255 ) {
   1094 				sample[j] = 255;
   1095 			}
   1096 			dv->color[j] = sample[j];
   1097 		}
   1098 
   1099 		// Don't bother writing alpha since it will already be set to 255,
   1100 		// plus we don't want to write over alpha generated by SetTerrainTextures
   1101 		//dv->color[3] = 255;
   1102 	}
   1103 }
   1104 
   1105 
   1106 /*
   1107 =================
   1108 LinearSubdivideMesh
   1109 
   1110 For extra lighting, just midpoint one of the axis.
   1111 The edges are clamped at the original edges.
   1112 =================
   1113 */
   1114 mesh_t *LinearSubdivideMesh( mesh_t *in ) {
   1115 	int			i, j;
   1116 	mesh_t		*out;
   1117 	drawVert_t	*v1, *v2, *vout;
   1118 
   1119 	out = malloc( sizeof( *out ) );
   1120 
   1121 	out->width = in->width * 2;
   1122 	out->height = in->height;
   1123 	out->verts = malloc( out->width * out->height * sizeof(*out->verts) );
   1124 	for ( j = 0 ; j < in->height ; j++ ) {
   1125 		out->verts[ j * out->width + 0 ] = in->verts[ j * in->width + 0 ];
   1126 		out->verts[ j * out->width + out->width - 1 ] = in->verts[ j * in->width + in->width - 1 ];
   1127 		for ( i = 1 ; i < out->width - 1 ; i+= 2 ) {
   1128 			v1 = in->verts + j * in->width + (i >> 1);
   1129 			v2 = v1 + 1;
   1130 			vout = out->verts + j * out->width + i;
   1131 
   1132 			vout->xyz[0] = 0.75 * v1->xyz[0] + 0.25 * v2->xyz[0];
   1133 			vout->xyz[1] = 0.75 * v1->xyz[1] + 0.25 * v2->xyz[1];
   1134 			vout->xyz[2] = 0.75 * v1->xyz[2] + 0.25 * v2->xyz[2];
   1135 
   1136 			vout->normal[0] = 0.75 * v1->normal[0] + 0.25 * v2->normal[0];
   1137 			vout->normal[1] = 0.75 * v1->normal[1] + 0.25 * v2->normal[1];
   1138 			vout->normal[2] = 0.75 * v1->normal[2] + 0.25 * v2->normal[2];
   1139 
   1140 			VectorNormalize( vout->normal, vout->normal );
   1141 
   1142 			vout++;
   1143 
   1144 			vout->xyz[0] = 0.25 * v1->xyz[0] + 0.75 * v2->xyz[0];
   1145 			vout->xyz[1] = 0.25 * v1->xyz[1] + 0.75 * v2->xyz[1];
   1146 			vout->xyz[2] = 0.25 * v1->xyz[2] + 0.75 * v2->xyz[2];
   1147 
   1148 			vout->normal[0] = 0.25 * v1->normal[0] + 0.75 * v2->normal[0];
   1149 			vout->normal[1] = 0.25 * v1->normal[1] + 0.75 * v2->normal[1];
   1150 			vout->normal[2] = 0.25 * v1->normal[2] + 0.75 * v2->normal[2];
   1151 
   1152 			VectorNormalize( vout->normal, vout->normal );
   1153 
   1154 		}
   1155 	}
   1156 
   1157 	FreeMesh( in );
   1158 
   1159 	return out;
   1160 }
   1161 
   1162 /*
   1163 ==============
   1164 ColorToBytes
   1165 ==============
   1166 */
   1167 void ColorToBytes( const float *color, byte *colorBytes ) {
   1168 	float	max;
   1169 	vec3_t	sample;
   1170 
   1171 	VectorCopy( color, sample );
   1172 
   1173 	// clamp with color normalization
   1174 	max = sample[0];
   1175 	if ( sample[1] > max ) {
   1176 		max = sample[1];
   1177 	}
   1178 	if ( sample[2] > max ) {
   1179 		max = sample[2];
   1180 	}
   1181 	if ( max > 255 ) {
   1182 		VectorScale( sample, 255/max, sample );
   1183 	}
   1184 	colorBytes[ 0 ] = sample[0];
   1185 	colorBytes[ 1 ] = sample[1];
   1186 	colorBytes[ 2 ] = sample[2];
   1187 }
   1188 
   1189 
   1190 
   1191 /*
   1192 =============
   1193 TraceLtm
   1194 =============
   1195 */
   1196 void TraceLtm( int num ) {
   1197 	dsurface_t	*ds;
   1198 	int			i, j, k;
   1199 	int			x, y;
   1200 	int			position, numPositions;
   1201 	vec3_t		base, origin, normal;
   1202 	byte		occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
   1203 	vec3_t		color[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
   1204 	traceWork_t	tw;
   1205 	vec3_t		average;
   1206 	int			count;
   1207 	mesh_t		srcMesh, *mesh, *subdivided;
   1208 	shaderInfo_t	*si;
   1209 	static float	nudge[2][9] = {
   1210 		{ 0, -1, 0, 1, -1, 1, -1, 0, 1 },
   1211 		{ 0, -1, -1, -1, 0, 0, 1, 1, 1 }
   1212 	};
   1213 	int			sampleWidth, sampleHeight, ssize;
   1214 	vec3_t		lightmapOrigin, lightmapVecs[2];
   1215 	int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_WIDTH];
   1216 
   1217 	ds = &drawSurfaces[num];
   1218 	si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
   1219 
   1220 	// vertex-lit triangle model
   1221 	if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
   1222 		VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
   1223 		return;
   1224 	}
   1225 	
   1226 	if ( ds->lightmapNum == -1 ) {
   1227 		return;		// doesn't need lighting at all
   1228 	}
   1229 
   1230 	if (!novertexlighting) {
   1231 		// calculate the vertex lighting for gouraud shade mode
   1232 		VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
   1233 	}
   1234 
   1235 	if ( ds->lightmapNum < 0 ) {
   1236 		return;		// doesn't need lightmap lighting
   1237 	}
   1238 
   1239 	si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
   1240 	ssize = samplesize;
   1241 	if (si->lightmapSampleSize)
   1242 		ssize = si->lightmapSampleSize;
   1243 
   1244 	if (si->patchShadows)
   1245 		tw.patchshadows = qtrue;
   1246 	else
   1247 		tw.patchshadows = patchshadows;
   1248 
   1249 	if ( ds->surfaceType == MST_PATCH ) {
   1250 		srcMesh.width = ds->patchWidth;
   1251 		srcMesh.height = ds->patchHeight;
   1252 		srcMesh.verts = drawVerts + ds->firstVert;
   1253 		mesh = SubdivideMesh( srcMesh, 8, 999 );
   1254 		PutMeshOnCurve( *mesh );
   1255 		MakeMeshNormals( *mesh );
   1256 
   1257 		subdivided = RemoveLinearMeshColumnsRows( mesh );
   1258 		FreeMesh(mesh);
   1259 
   1260 		mesh = SubdivideMeshQuads( subdivided, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
   1261 		if ( mesh->width != ds->lightmapWidth || mesh->height != ds->lightmapHeight ) {
   1262 			Error( "Mesh lightmap miscount");
   1263 		}
   1264 
   1265 		if ( extra ) {
   1266 			mesh_t	*mp;
   1267 
   1268 			// chop it up for more light samples (leaking memory...)
   1269 			mp = mesh;//CopyMesh( mesh );
   1270 			mp = LinearSubdivideMesh( mp );
   1271 			mp = TransposeMesh( mp );
   1272 			mp = LinearSubdivideMesh( mp );
   1273 			mp = TransposeMesh( mp );
   1274 
   1275 			mesh = mp;
   1276 		}
   1277 	} else {
   1278 		VectorCopy( ds->lightmapVecs[2], normal );
   1279 
   1280 		if ( !extra ) {
   1281 			VectorCopy( ds->lightmapOrigin, lightmapOrigin );
   1282 			VectorCopy( ds->lightmapVecs[0], lightmapVecs[0] );
   1283 			VectorCopy( ds->lightmapVecs[1], lightmapVecs[1] );
   1284 		} else {
   1285 			// sample at a closer spacing for antialiasing
   1286 			VectorCopy( ds->lightmapOrigin, lightmapOrigin );
   1287 			VectorScale( ds->lightmapVecs[0], 0.5, lightmapVecs[0] );
   1288 			VectorScale( ds->lightmapVecs[1], 0.5, lightmapVecs[1] );
   1289 			VectorMA( lightmapOrigin, -0.5, lightmapVecs[0], lightmapOrigin );
   1290 			VectorMA( lightmapOrigin, -0.5, lightmapVecs[1], lightmapOrigin );
   1291 		}
   1292 	}
   1293 
   1294 	if ( extra ) {
   1295 		sampleWidth = ds->lightmapWidth * 2;
   1296 		sampleHeight = ds->lightmapHeight * 2;
   1297 	} else {
   1298 		sampleWidth = ds->lightmapWidth;
   1299 		sampleHeight = ds->lightmapHeight;
   1300 	}
   1301 
   1302 	memset ( color, 0, sizeof( color ) );
   1303 
   1304 	// determine which samples are occluded
   1305 	memset ( occluded, 0, sizeof( occluded ) );
   1306 	for ( i = 0 ; i < sampleWidth ; i++ ) {
   1307 		for ( j = 0 ; j < sampleHeight ; j++ ) {
   1308 
   1309 			if ( ds->patchWidth ) {
   1310 				numPositions = 9;
   1311 				VectorCopy( mesh->verts[j*mesh->width+i].normal, normal );
   1312 				// VectorNormalize( normal, normal );
   1313 				// push off of the curve a bit
   1314 				VectorMA( mesh->verts[j*mesh->width+i].xyz, 1, normal, base );
   1315 
   1316 				MakeNormalVectors( normal, lightmapVecs[0], lightmapVecs[1] );
   1317 			} else {
   1318 				numPositions = 9;
   1319 				for ( k = 0 ; k < 3 ; k++ ) {
   1320 					base[k] = lightmapOrigin[k] + normal[k]
   1321 						+ i * lightmapVecs[0][k] 
   1322 						+ j * lightmapVecs[1][k];
   1323 				}
   1324 			}
   1325 			VectorAdd( base, surfaceOrigin[ num ], base );
   1326 
   1327 			// we may need to slightly nudge the sample point
   1328 			// if directly on a wall
   1329 			for ( position = 0 ; position < numPositions ; position++ ) {
   1330 				// calculate lightmap sample position
   1331 				for ( k = 0 ; k < 3 ; k++ ) {
   1332 					origin[k] = base[k] + 
   1333 						+ ( nudge[0][position]/16 ) * lightmapVecs[0][k] 
   1334 						+ ( nudge[1][position]/16 ) * lightmapVecs[1][k];
   1335 				}
   1336 
   1337 				if ( notrace ) {
   1338 					break;
   1339 				}
   1340 				if ( !PointInSolid( origin ) ) {
   1341 					break;
   1342 				}
   1343 			}
   1344 
   1345 			// if none of the nudges worked, this sample is occluded
   1346 			if ( position == numPositions ) {
   1347 				occluded[i][j] = qtrue;
   1348 				if ( numthreads == 1 ) {
   1349 					c_occluded++;
   1350 				}
   1351 				continue;
   1352 			}
   1353 			
   1354 			if ( numthreads == 1 ) {
   1355 				c_visible++;
   1356 			}
   1357 			occluded[i][j] = qfalse;
   1358 			LightingAtSample( origin, normal, color[i][j], qtrue, qfalse, &tw );
   1359 		}
   1360 	}
   1361 
   1362 	if ( dump ) {
   1363 		PrintOccluded( occluded, sampleWidth, sampleHeight );
   1364 	}
   1365 
   1366 	// calculate average values for occluded samples
   1367 	for ( i = 0 ; i < sampleWidth ; i++ ) {
   1368 		for ( j = 0 ; j < sampleHeight ; j++ ) {
   1369 			if ( !occluded[i][j] ) {
   1370 				continue;
   1371 			}
   1372 			// scan all surrounding samples
   1373 			count = 0;
   1374 			VectorClear( average );
   1375 			for ( x = -1 ; x <= 1; x++ ) {
   1376 				for ( y = -1 ; y <= 1 ; y++ ) {
   1377 					if ( i + x < 0 || i + x >= sampleWidth ) {
   1378 						continue;
   1379 					}
   1380 					if ( j + y < 0 || j + y >= sampleHeight ) {
   1381 						continue;
   1382 					}
   1383 					if ( occluded[i+x][j+y] ) {
   1384 						continue;
   1385 					}
   1386 					count++;
   1387 					VectorAdd( color[i+x][j+y], average, average );
   1388 				}
   1389 			}
   1390 			if ( count ) {
   1391 				VectorScale( average, 1.0/count, color[i][j] );
   1392 			}
   1393 		}
   1394 	}
   1395 
   1396 	// average together the values if we are extra sampling
   1397 	if ( ds->lightmapWidth != sampleWidth ) {
   1398 		for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
   1399 			for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
   1400 				for ( k = 0 ; k < 3 ; k++ ) {
   1401 					float		value, coverage;
   1402 
   1403 					value = color[i*2][j*2][k] + color[i*2][j*2+1][k] +
   1404 						color[i*2+1][j*2][k] + color[i*2+1][j*2+1][k];
   1405 					coverage = 4;
   1406 					if ( extraWide ) {
   1407 						// wider than box filter
   1408 						if ( i > 0 ) {
   1409 							value += color[i*2-1][j*2][k] + color[i*2-1][j*2+1][k];
   1410 							value += color[i*2-2][j*2][k] + color[i*2-2][j*2+1][k];
   1411 							coverage += 4;
   1412 						}
   1413 						if ( i < ds->lightmapWidth - 1 ) {
   1414 							value += color[i*2+2][j*2][k] + color[i*2+2][j*2+1][k];
   1415 							value += color[i*2+3][j*2][k] + color[i*2+3][j*2+1][k];
   1416 							coverage += 4;
   1417 						}
   1418 						if ( j > 0 ) {
   1419 							value += color[i*2][j*2-1][k] + color[i*2+1][j*2-1][k];
   1420 							value += color[i*2][j*2-2][k] + color[i*2+1][j*2-2][k];
   1421 							coverage += 4;
   1422 						}
   1423 						if ( j < ds->lightmapHeight - 1 ) {
   1424 							value += color[i*2][j*2+2][k] + color[i*2+1][j*2+2][k];
   1425 							value += color[i*2][j*2+3][k] + color[i*2+1][j*2+3][k];
   1426 							coverage += 2;
   1427 						}
   1428 					}
   1429 
   1430 					color[i][j][k] = value / coverage;
   1431 				}
   1432 			}
   1433 		}
   1434 	}
   1435 
   1436 	// optionally create a debugging border around the lightmap
   1437 	if ( lightmapBorder ) {
   1438 		for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
   1439 			color[i][0][0] = 255;
   1440 			color[i][0][1] = 0;
   1441 			color[i][0][2] = 0;
   1442 
   1443 			color[i][ds->lightmapHeight-1][0] = 255;
   1444 			color[i][ds->lightmapHeight-1][1] = 0;
   1445 			color[i][ds->lightmapHeight-1][2] = 0;
   1446 		}
   1447 		for ( i = 0 ; i < ds->lightmapHeight ; i++ ) {
   1448 			color[0][i][0] = 255;
   1449 			color[0][i][1] = 0;
   1450 			color[0][i][2] = 0;
   1451 
   1452 			color[ds->lightmapWidth-1][i][0] = 255;
   1453 			color[ds->lightmapWidth-1][i][1] = 0;
   1454 			color[ds->lightmapWidth-1][i][2] = 0;
   1455 		}
   1456 	}
   1457 
   1458 	// clamp the colors to bytes and store off
   1459 	for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
   1460 		for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
   1461 			k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + j) 
   1462 				* LIGHTMAP_WIDTH + ds->lightmapX + i;
   1463 
   1464 			ColorToBytes( color[i][j], lightBytes + k*3 );
   1465 		}
   1466 	}
   1467 
   1468 	if (ds->surfaceType == MST_PATCH)
   1469 	{
   1470 		FreeMesh(mesh);
   1471 	}
   1472 }
   1473 
   1474 
   1475 //=============================================================================
   1476 
   1477 vec3_t	gridMins;
   1478 vec3_t	gridSize = { 64, 64, 128 };
   1479 int		gridBounds[3];
   1480 
   1481 
   1482 /*
   1483 ========================
   1484 LightContributionToPoint
   1485 ========================
   1486 */
   1487 qboolean LightContributionToPoint( const light_t *light, const vec3_t origin,
   1488 								  vec3_t color, traceWork_t *tw ) {
   1489 	trace_t		trace;
   1490 	float		add;
   1491 
   1492 	add = 0;
   1493 
   1494 	VectorClear( color );
   1495 
   1496 	// testing exact PTPFF
   1497 	if ( exactPointToPolygon && light->type == emit_area ) {
   1498 		float		factor;
   1499 		float		d;
   1500 		vec3_t		normal;
   1501 
   1502 		// see if the point is behind the light
   1503 		d = DotProduct( origin, light->normal ) - light->dist;
   1504 		if ( !light->twosided ) {
   1505 			if ( d < 1 ) {
   1506 				return qfalse;		// point is behind light
   1507 			}
   1508 		}
   1509 
   1510 		// test occlusion
   1511 		// clip the line, tracing from the surface towards the light
   1512 		TraceLine( origin, light->origin, &trace, qfalse, tw );
   1513 		if ( trace.passSolid ) {
   1514 			return qfalse;
   1515 		}
   1516 
   1517 		// calculate the contribution
   1518 		VectorSubtract( light->origin, origin, normal );
   1519 		if ( VectorNormalize( normal, normal ) == 0 ) {
   1520 			return qfalse;
   1521 		}
   1522 		factor = PointToPolygonFormFactor( origin, normal, light->w );
   1523 		if ( factor <= 0 ) {
   1524 			if ( light->twosided ) {
   1525 				factor = -factor;
   1526 			} else {
   1527 				return qfalse;
   1528 			}
   1529 		}
   1530 		VectorScale( light->emitColor, factor, color );
   1531 		return qtrue;
   1532 	}
   1533 
   1534 	// calculate the amount of light at this sample
   1535 	if ( light->type == emit_point || light->type == emit_spotlight ) {
   1536 		vec3_t		dir;
   1537 		float		dist;
   1538 
   1539 		VectorSubtract( light->origin, origin, dir );
   1540 		dist = VectorLength( dir );
   1541 		// clamp the distance to prevent super hot spots
   1542 		if ( dist < 16 ) {
   1543 			dist = 16;
   1544 		}
   1545 		if ( light->linearLight ) {
   1546 			add = light->photons * linearScale - dist;
   1547 			if ( add < 0 ) {
   1548 				add = 0;
   1549 			}
   1550 		} else {
   1551 			add = light->photons / ( dist * dist );
   1552 		}
   1553 	} else {
   1554 		return qfalse;
   1555 	}
   1556 
   1557 	if ( add <= 1.0 ) {
   1558 		return qfalse;
   1559 	}
   1560 
   1561 	// clip the line, tracing from the surface towards the light
   1562 	TraceLine( origin, light->origin, &trace, qfalse, tw );
   1563 
   1564 	// other light rays must not hit anything
   1565 	if ( trace.passSolid ) {
   1566 		return qfalse;
   1567 	}
   1568 
   1569 	// add the result
   1570 	color[0] = add * light->color[0];
   1571 	color[1] = add * light->color[1];
   1572 	color[2] = add * light->color[2];
   1573 
   1574 	return qtrue;
   1575 }
   1576 
   1577 typedef struct {
   1578 	vec3_t		dir;
   1579 	vec3_t		color;
   1580 } contribution_t;
   1581 
   1582 /*
   1583 =============
   1584 TraceGrid
   1585 
   1586 Grid samples are foe quickly determining the lighting
   1587 of dynamically placed entities in the world
   1588 =============
   1589 */
   1590 #define	MAX_CONTRIBUTIONS	1024
   1591 void TraceGrid( int num ) {
   1592 	int			x, y, z;
   1593 	vec3_t		origin;
   1594 	light_t		*light;
   1595 	vec3_t		color;
   1596 	int			mod;
   1597 	vec3_t		directedColor;
   1598 	vec3_t		summedDir;
   1599 	contribution_t	contributions[MAX_CONTRIBUTIONS];
   1600 	int			numCon;
   1601 	int			i;
   1602 	traceWork_t	tw;
   1603 	float		addSize;
   1604 
   1605 	mod = num;
   1606 	z = mod / ( gridBounds[0] * gridBounds[1] );
   1607 	mod -= z * ( gridBounds[0] * gridBounds[1] );
   1608 
   1609 	y = mod / gridBounds[0];
   1610 	mod -= y * gridBounds[0];
   1611 
   1612 	x = mod;
   1613 
   1614 	origin[0] = gridMins[0] + x * gridSize[0];
   1615 	origin[1] = gridMins[1] + y * gridSize[1];
   1616 	origin[2] = gridMins[2] + z * gridSize[2];
   1617 
   1618 	if ( PointInSolid( origin ) ) {
   1619 		vec3_t	baseOrigin;
   1620 		int		step;
   1621 
   1622 		VectorCopy( origin, baseOrigin );
   1623 
   1624 		// try to nudge the origin around to find a valid point
   1625 		for ( step = 9 ; step <= 18 ; step += 9 ) {
   1626 			for ( i = 0 ; i < 8 ; i++ ) {
   1627 				VectorCopy( baseOrigin, origin );
   1628 				if ( i & 1 ) {
   1629 					origin[0] += step;
   1630 				} else {
   1631 					origin[0] -= step;
   1632 				}
   1633 				if ( i & 2 ) {
   1634 					origin[1] += step;
   1635 				} else {
   1636 					origin[1] -= step;
   1637 				}
   1638 				if ( i & 4 ) {
   1639 					origin[2] += step;
   1640 				} else {
   1641 					origin[2] -= step;
   1642 				}
   1643 
   1644 				if ( !PointInSolid( origin ) ) {
   1645 					break;
   1646 				}
   1647 			}
   1648 			if ( i != 8 ) {
   1649 				break;
   1650 			}
   1651 		}
   1652 		if ( step > 18 ) {
   1653 			// can't find a valid point at all
   1654 			for ( i = 0 ; i < 8 ; i++ ) {
   1655 				gridData[ num*8 + i ] = 0;
   1656 			}
   1657 			return;
   1658 		}
   1659 	}
   1660 
   1661 	VectorClear( summedDir );
   1662 
   1663 	// trace to all the lights
   1664 
   1665 	// find the major light direction, and divide the
   1666 	// total light between that along the direction and
   1667 	// the remaining in the ambient 
   1668 	numCon = 0;
   1669 	for ( light = lights ; light ; light = light->next ) {
   1670 		vec3_t		add;
   1671 		vec3_t		dir;
   1672 		float		addSize;
   1673 
   1674 		if ( !LightContributionToPoint( light, origin, add, &tw ) ) {
   1675 			continue;
   1676 		}
   1677 
   1678 		VectorSubtract( light->origin, origin, dir );
   1679 		VectorNormalize( dir, dir );
   1680 
   1681 		VectorCopy( add, contributions[numCon].color );
   1682 		VectorCopy( dir, contributions[numCon].dir );
   1683 		numCon++;
   1684 
   1685 		addSize = VectorLength( add );
   1686 		VectorMA( summedDir, addSize, dir, summedDir );
   1687 
   1688 		if ( numCon == MAX_CONTRIBUTIONS-1 ) {
   1689 			break;
   1690 		}
   1691 	}
   1692 
   1693 	//
   1694 	// trace directly to the sun
   1695 	//
   1696 	SunToPoint( origin, &tw, color );
   1697 	addSize = VectorLength( color );
   1698 	if ( addSize > 0 ) {
   1699 		VectorCopy( color, contributions[numCon].color );
   1700 		VectorCopy( sunDirection, contributions[numCon].dir );
   1701 		VectorMA( summedDir, addSize, sunDirection, summedDir );
   1702 		numCon++;
   1703 	}
   1704 
   1705 
   1706 	// now that we have identified the primary light direction,
   1707 	// go back and seperate all the light into directed and ambient
   1708 	VectorNormalize( summedDir, summedDir );
   1709 	VectorCopy( ambientColor, color );
   1710 	VectorClear( directedColor );
   1711 
   1712 	for ( i = 0 ; i < numCon ; i++ ) {
   1713 		float	d;
   1714 
   1715 		d = DotProduct( contributions[i].dir, summedDir );
   1716 		if ( d < 0 ) {
   1717 			d = 0;
   1718 		}
   1719 
   1720 		VectorMA( directedColor, d, contributions[i].color, directedColor );
   1721 
   1722 		// the ambient light will be at 1/4 the value of directed light
   1723 		d = 0.25 * ( 1.0 - d );
   1724 		VectorMA( color, d, contributions[i].color, color );
   1725 	}
   1726 
   1727 	// now do some fudging to keep the ambient from being too low
   1728 	VectorMA( color, 0.25, directedColor, color );
   1729 
   1730 	//
   1731 	// save the resulting value out
   1732 	//
   1733 	ColorToBytes( color, gridData + num*8 );
   1734 	ColorToBytes( directedColor, gridData + num*8 + 3 );
   1735 
   1736 	VectorNormalize( summedDir, summedDir );
   1737 	NormalToLatLong( summedDir, gridData + num*8 + 6);
   1738 }
   1739 
   1740 
   1741 /*
   1742 =============
   1743 SetupGrid
   1744 =============
   1745 */
   1746 void SetupGrid( void ) {
   1747 	int		i;
   1748 	vec3_t	maxs;
   1749 
   1750 	for ( i = 0 ; i < 3 ; i++ ) {
   1751 		gridMins[i] = gridSize[i] * ceil( dmodels[0].mins[i] / gridSize[i] );
   1752 		maxs[i] = gridSize[i] * floor( dmodels[0].maxs[i] / gridSize[i] );
   1753 		gridBounds[i] = (maxs[i] - gridMins[i])/gridSize[i] + 1;
   1754 	}
   1755 
   1756 	numGridPoints = gridBounds[0] * gridBounds[1] * gridBounds[2];
   1757 	if (numGridPoints * 8 >= MAX_MAP_LIGHTGRID)
   1758 		Error("MAX_MAP_LIGHTGRID");
   1759 	qprintf( "%5i gridPoints\n", numGridPoints );
   1760 }
   1761 
   1762 //=============================================================================
   1763 
   1764 /*
   1765 =============
   1766 RemoveLightsInSolid
   1767 =============
   1768 */
   1769 void RemoveLightsInSolid(void)
   1770 {
   1771 	light_t *light, *prev;
   1772 	int numsolid = 0;
   1773 
   1774 	prev = NULL;
   1775 	for ( light = lights ; light ;  ) {
   1776 		if (PointInSolid(light->origin))
   1777 		{
   1778 			if (prev) prev->next = light->next;
   1779 			else lights = light->next;
   1780 			if (light->w)
   1781 				FreeWinding(light->w);
   1782 			free(light);
   1783 			numsolid++;
   1784 			if (prev)
   1785 				light = prev->next;
   1786 			else
   1787 				light = lights;
   1788 		}
   1789 		else
   1790 		{
   1791 			prev = light;
   1792 			light = light->next;
   1793 		}
   1794 	}
   1795 	_printf (" %7i lights in solid\n", numsolid);
   1796 }
   1797 
   1798 /*
   1799 =============
   1800 LightWorld
   1801 =============
   1802 */
   1803 void LightWorld (void) {
   1804 	float		f;
   1805 
   1806 	// determine the number of grid points
   1807 	SetupGrid();
   1808 
   1809 	// find the optional world ambient
   1810 	GetVectorForKey( &entities[0], "_color", ambientColor );
   1811 	f = FloatForKey( &entities[0], "ambient" );
   1812 	VectorScale( ambientColor, f, ambientColor );
   1813 
   1814 	// create lights out of patches and lights
   1815 	qprintf ("--- CreateLights ---\n");
   1816 	CreateEntityLights ();
   1817 	qprintf ("%i point lights\n", numPointLights);
   1818 	qprintf ("%i area lights\n", numAreaLights);
   1819 
   1820 	if (!nogridlighting) {
   1821 		qprintf ("--- TraceGrid ---\n");
   1822 		RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
   1823 		qprintf( "%i x %i x %i = %i grid\n", gridBounds[0], gridBounds[1],
   1824 			gridBounds[2], numGridPoints);
   1825 	}
   1826 
   1827 	qprintf ("--- TraceLtm ---\n");
   1828 	RunThreadsOnIndividual( numDrawSurfaces, qtrue, TraceLtm );
   1829 	qprintf( "%5i visible samples\n", c_visible );
   1830 	qprintf( "%5i occluded samples\n", c_occluded );
   1831 }
   1832 
   1833 /*
   1834 ========
   1835 CreateFilters
   1836 
   1837 EXPERIMENTAL, UNUSED
   1838 
   1839 Look for transparent light filter surfaces.
   1840 
   1841 This will only work for flat 3*3 patches that exactly hold one copy of the texture.
   1842 ========
   1843 */
   1844 #define	PLANAR_PATCH_EPSILON	0.1
   1845 void CreateFilters( void ) {
   1846 	int				i;
   1847 	filter_t		*f;
   1848 	dsurface_t		*ds;
   1849 	shaderInfo_t	*si;
   1850 	drawVert_t		*v1, *v2, *v3;
   1851 	vec3_t			d1, d2;
   1852 	int				vertNum;
   1853 
   1854 	numFilters = 0;
   1855 
   1856 	return;
   1857 
   1858 	for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
   1859 		ds = &drawSurfaces[i];
   1860 		if ( !ds->patchWidth ) {
   1861 			continue;
   1862 		}
   1863 		si = ShaderInfoForShader( dshaders[ ds->shaderNum ].shader );
   1864 /*
   1865 		if ( !(si->surfaceFlags & SURF_LIGHTFILTER) ) {
   1866 			continue;
   1867 		}
   1868 */
   1869 
   1870 		// we have a filter patch
   1871 		v1 = &drawVerts[ ds->firstVert ];
   1872 
   1873 		if ( ds->patchWidth != 3 || ds->patchHeight != 3 ) {
   1874 			_printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't a 3 by 3\n",
   1875 				v1->xyz[0], v1->xyz[1], v1->xyz[2] );
   1876 			continue;
   1877 		}
   1878 
   1879 		if ( numFilters == MAX_FILTERS ) {
   1880 			Error( "MAX_FILTERS" );
   1881 		}
   1882 		f = &filters[ numFilters ];
   1883 		numFilters++;
   1884 
   1885 		v2 = &drawVerts[ ds->firstVert + 2 ];
   1886 		v3 = &drawVerts[ ds->firstVert + 6 ];
   1887 
   1888 		VectorSubtract( v2->xyz, v1->xyz, d1 );
   1889 		VectorSubtract( v3->xyz, v1->xyz, d2 );
   1890 		VectorNormalize( d1, d1 );
   1891 		VectorNormalize( d2, d2 );
   1892 		CrossProduct( d1, d2, f->plane );
   1893 		f->plane[3] = DotProduct( v1->xyz, f->plane );
   1894 
   1895 		// make sure all the control points are on the plane
   1896 		for ( vertNum = 0 ; vertNum < ds->numVerts ; vertNum++ ) {
   1897 			float	d;
   1898 
   1899 			d = DotProduct( drawVerts[ ds->firstVert + vertNum ].xyz, f->plane ) - f->plane[3];
   1900 			if ( fabs( d ) > PLANAR_PATCH_EPSILON ) {
   1901 				break;
   1902 			}
   1903 		}
   1904 		if ( vertNum != ds->numVerts ) {
   1905 			numFilters--;
   1906 			_printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't flat\n",
   1907 				v1->xyz[0], v1->xyz[1], v1->xyz[2] );
   1908 			continue;
   1909 		}
   1910 	}
   1911 
   1912 	f = &filters[0];
   1913 	numFilters = 1;
   1914 
   1915 	f->plane[0] = 1;
   1916 	f->plane[1] = 0;
   1917 	f->plane[2] = 0;
   1918 	f->plane[3] = 448;
   1919 
   1920 	f->origin[0] = 448;
   1921 	f->origin[1] = 192;
   1922 	f->origin[2] = 0;
   1923 
   1924 	f->vectors[0][0] = 0;
   1925 	f->vectors[0][1] = -1.0 / 128;
   1926 	f->vectors[0][2] = 0;
   1927 
   1928 	f->vectors[1][0] = 0;
   1929 	f->vectors[1][1] = 0;
   1930 	f->vectors[1][2] = 1.0 / 128;
   1931 
   1932 	f->si = ShaderInfoForShader( "textures/hell/blocks11ct" );
   1933 }
   1934 
   1935 /*
   1936 =============
   1937 VertexLightingThread
   1938 =============
   1939 */
   1940 void VertexLightingThread(int num) {
   1941 	dsurface_t	*ds;
   1942 	traceWork_t	tw;
   1943 	shaderInfo_t *si;
   1944 
   1945 	ds = &drawSurfaces[num];
   1946 
   1947 	// vertex-lit triangle model
   1948 	if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
   1949 		return;
   1950 	}
   1951 
   1952 	if (novertexlighting)
   1953 		return;
   1954 
   1955 	if ( ds->lightmapNum == -1 ) {
   1956 		return;	// doesn't need lighting at all
   1957 	}
   1958 
   1959 	si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
   1960 
   1961 	// calculate the vertex lighting for gouraud shade mode
   1962 	VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
   1963 }
   1964 
   1965 /*
   1966 =============
   1967 TriSoupLightingThread
   1968 =============
   1969 */
   1970 void TriSoupLightingThread(int num) {
   1971 	dsurface_t	*ds;
   1972 	traceWork_t	tw;
   1973 	shaderInfo_t *si;
   1974 
   1975 	ds = &drawSurfaces[num];
   1976 	si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
   1977 
   1978 	// vertex-lit triangle model
   1979 	if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
   1980 		VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
   1981 	}
   1982 }
   1983 
   1984 /*
   1985 =============
   1986 GridAndVertexLighting
   1987 =============
   1988 */
   1989 void GridAndVertexLighting(void) {
   1990 	SetupGrid();
   1991 
   1992 	FindSkyBrushes();
   1993 	CreateFilters();
   1994 	InitTrace();
   1995 	CreateEntityLights ();
   1996 	CreateSurfaceLights();
   1997 
   1998 	if (!nogridlighting) {
   1999 		_printf ("--- TraceGrid ---\n");
   2000 		RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
   2001 	}
   2002 
   2003 	if (!novertexlighting) {
   2004 		_printf ("--- Vertex Lighting ---\n");
   2005 		RunThreadsOnIndividual( numDrawSurfaces, qtrue, VertexLightingThread );
   2006 	}
   2007 
   2008 	_printf("--- Model Lighting ---\n");
   2009 	RunThreadsOnIndividual( numDrawSurfaces, qtrue, TriSoupLightingThread );
   2010 }
   2011 
   2012 /*
   2013 ========
   2014 LightMain
   2015 
   2016 ========
   2017 */
   2018 int LightMain (int argc, char **argv) {
   2019 	int			i;
   2020 	double		start, end;
   2021 	const char	*value;
   2022 
   2023 	_printf ("----- Lighting ----\n");
   2024 
   2025 	verbose = qfalse;
   2026 
   2027 	for (i=1 ; i<argc ; i++) {
   2028 		if (!strcmp(argv[i],"-tempname"))
   2029     {
   2030       i++;
   2031     } else if (!strcmp(argv[i],"-v")) {
   2032 			verbose = qtrue;
   2033 		} else if (!strcmp(argv[i],"-threads")) {
   2034 			numthreads = atoi (argv[i+1]);
   2035 			i++;
   2036 		} else if (!strcmp(argv[i],"-area")) {
   2037 			areaScale *= atof(argv[i+1]);
   2038 			_printf ("area light scaling at %f\n", areaScale);
   2039 			i++;
   2040 		} else if (!strcmp(argv[i],"-point")) {
   2041 			pointScale *= atof(argv[i+1]);
   2042 			_printf ("point light scaling at %f\n", pointScale);
   2043 			i++;
   2044 		} else if (!strcmp(argv[i],"-notrace")) {
   2045 			notrace = qtrue;
   2046 			_printf ("No occlusion tracing\n");
   2047 		} else if (!strcmp(argv[i],"-patchshadows")) {
   2048 			patchshadows = qtrue;
   2049 			_printf ("Patch shadow casting enabled\n");
   2050 		} else if (!strcmp(argv[i],"-extra")) {
   2051 			extra = qtrue;
   2052 			_printf ("Extra detail tracing\n");
   2053 		} else if (!strcmp(argv[i],"-extrawide")) {
   2054 			extra = qtrue;
   2055 			extraWide = qtrue;
   2056 			_printf ("Extra wide detail tracing\n");
   2057 		} else if (!strcmp(argv[i], "-samplesize")) {
   2058 			samplesize = atoi(argv[i+1]);
   2059 			if (samplesize < 1) samplesize = 1;
   2060 			i++;
   2061 			_printf("lightmap sample size is %dx%d units\n", samplesize, samplesize);
   2062 		} else if (!strcmp(argv[i], "-novertex")) {
   2063 			novertexlighting = qtrue;
   2064 			_printf("no vertex lighting = true\n");
   2065 		} else if (!strcmp(argv[i], "-nogrid")) {
   2066 			nogridlighting = qtrue;
   2067 			_printf("no grid lighting = true\n");
   2068 		} else if (!strcmp(argv[i],"-border")) {
   2069 			lightmapBorder = qtrue;
   2070 			_printf ("Adding debug border to lightmaps\n");
   2071 		} else if (!strcmp(argv[i],"-nosurf")) {
   2072 			noSurfaces = qtrue;
   2073 			_printf ("Not tracing against surfaces\n" );
   2074 		} else if (!strcmp(argv[i],"-dump")) {
   2075 			dump = qtrue;
   2076 			_printf ("Dumping occlusion maps\n");
   2077 		} else {
   2078 			break;
   2079 		}
   2080 	}
   2081 
   2082 	ThreadSetDefault ();
   2083 
   2084 	if (i != argc - 1) {
   2085 		_printf("usage: q3map -light [-<switch> [-<switch> ...]] <mapname>\n"
   2086 				"\n"
   2087 				"Switches:\n"
   2088 				"   v              = verbose output\n"
   2089 				"   threads <X>    = set number of threads to X\n"
   2090 				"   area <V>       = set the area light scale to V\n"
   2091 				"   point <W>      = set the point light scale to W\n"
   2092 				"   notrace        = don't cast any shadows\n"
   2093 				"   extra          = enable super sampling for anti-aliasing\n"
   2094 				"   extrawide      = same as extra but smoothen more\n"
   2095 				"   nogrid         = don't calculate light grid for dynamic model lighting\n"
   2096 				"   novertex       = don't calculate vertex lighting\n"
   2097 				"   samplesize <N> = set the lightmap pixel size to NxN units\n");
   2098 		exit(0);
   2099 	}
   2100 
   2101 	start = I_FloatTime ();
   2102 
   2103 	SetQdirFromPath (argv[i]);	
   2104 
   2105 #ifdef _WIN32
   2106 	InitPakFile(gamedir, NULL);
   2107 #endif
   2108 
   2109 	strcpy (source, ExpandArg(argv[i]));
   2110 	StripExtension (source);
   2111 	DefaultExtension (source, ".bsp");
   2112 
   2113 	LoadShaderInfo();
   2114 
   2115 	_printf ("reading %s\n", source);
   2116 
   2117 	LoadBSPFile (source);
   2118 
   2119 	FindSkyBrushes();
   2120 
   2121 	ParseEntities();
   2122 
   2123 	value = ValueForKey( &entities[0], "gridsize" );
   2124 	if (strlen(value)) {
   2125 		sscanf( value, "%f %f %f", &gridSize[0], &gridSize[1], &gridSize[2] );
   2126 		_printf("grid size = {%1.1f, %1.1f, %1.1f}\n", gridSize[0], gridSize[1], gridSize[2]);
   2127 	}
   2128 
   2129 	CreateFilters();
   2130 
   2131 	InitTrace();
   2132 
   2133 	SetEntityOrigins();
   2134 
   2135 	CountLightmaps();
   2136 
   2137 	CreateSurfaceLights();
   2138 
   2139 	LightWorld();
   2140 
   2141 	_printf ("writing %s\n", source);
   2142 	WriteBSPFile (source);
   2143 
   2144 	end = I_FloatTime ();
   2145 	_printf ("%5.0f seconds elapsed\n", end-start);
   2146 	
   2147 	return 0;
   2148 }
   2149