1 /* -------------------------------------------------------------------------------
2 
3 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 
6 This file is part of GtkRadiant.
7 
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12 
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 
22 ----------------------------------------------------------------------------------
23 
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26 
27 ------------------------------------------------------------------------------- */
28 
29 
30 
31 /* marker */
32 #define LIGHTMAPS_YDNAR_C
33 
34 
35 
36 /* dependencies */
37 #include "q3map2.h"
38 
39 
40 
41 
42 /* -------------------------------------------------------------------------------
43 
44 this file contains code that doe lightmap allocation and projection that
45 runs in the -light phase.
46 
47 this is handled here rather than in the bsp phase for a few reasons--
48 surfaces are no longer necessarily convex polygons, patches may or may not be
49 planar or have lightmaps projected directly onto control points.
50 
51 also, this allows lightmaps to be calculated before being allocated and stored
52 in the bsp. lightmaps that have little high-frequency information are candidates
53 for having their resolutions scaled down.
54 
55 ------------------------------------------------------------------------------- */
56 
57 /*
58 WriteTGA24()
59 based on WriteTGA() from imagelib.c
60 */
61 
WriteTGA24(char * filename,byte * data,int width,int height,qboolean flip)62 void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip )
63 {
64 	int		i, c;
65 	byte	*buffer, *in;
66 	FILE	*file;
67 
68 
69 	/* allocate a buffer and set it up */
70 	buffer = safe_malloc( width * height * 3 + 18 );
71 	memset( buffer, 0, 18 );
72 	buffer[ 2 ] = 2;
73 	buffer[ 12 ] = width & 255;
74 	buffer[ 13 ] = width >> 8;
75 	buffer[ 14 ] = height & 255;
76 	buffer[ 15 ] = height >> 8;
77 	buffer[ 16 ] = 24;
78 
79 	/* swap rgb to bgr */
80 	c = (width * height * 3) + 18;
81 	for( i = 18; i < c; i += 3 )
82 	{
83 		buffer[ i ] = data[ i - 18 + 2 ];		/* blue */
84 		buffer[ i + 1 ] = data[ i - 18 + 1 ];	/* green */
85 		buffer[ i + 2 ] = data[ i - 18 + 0 ];	/* red */
86 	}
87 
88 	/* write it and free the buffer */
89 	file = fopen( filename, "wb" );
90 	if( file == NULL )
91 		Error( "Unable to open %s for writing", filename );
92 
93 	/* flip vertically? */
94 	if( flip )
95 	{
96 		fwrite( buffer, 1, 18, file );
97 		for( in = buffer + ((height - 1) * width * 3) + 18; in >= buffer; in -= (width * 3) )
98 			fwrite( in, 1, (width * 3), file );
99 	}
100 	else
101 		fwrite( buffer, 1, c, file );
102 
103 	/* close the file */
104 	fclose( file );
105 	free( buffer );
106 }
107 
108 
109 
110 /*
111 ExportLightmaps()
112 exports the lightmaps as a list of numbered tga images
113 */
114 
ExportLightmaps(void)115 void ExportLightmaps( void )
116 {
117 	int			i;
118 	char		dirname[ 1024 ], filename[ 1024 ];
119 	byte		*lightmap;
120 
121 
122 	/* note it */
123 	Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n");
124 
125 	/* do some path mangling */
126 	strcpy( dirname, source );
127 	StripExtension( dirname );
128 
129 	/* sanity check */
130 	if( bspLightBytes == NULL )
131 	{
132 		Sys_Printf( "WARNING: No BSP lightmap data\n" );
133 		return;
134 	}
135 
136 	/* make a directory for the lightmaps */
137 	Q_mkdir( dirname );
138 
139 	/* iterate through the lightmaps */
140 	for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (game->lightmapSize * game->lightmapSize * 3) )
141 	{
142 		/* write a tga image out */
143 		sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
144 		Sys_Printf( "Writing %s\n", filename );
145 		WriteTGA24( filename, lightmap, game->lightmapSize, game->lightmapSize, qfalse );
146 	}
147 }
148 
149 
150 
151 /*
152 ExportLightmapsMain()
153 exports the lightmaps as a list of numbered tga images
154 */
155 
ExportLightmapsMain(int argc,char ** argv)156 int ExportLightmapsMain( int argc, char **argv )
157 {
158 	/* arg checking */
159 	if( argc < 1 )
160 	{
161 		Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
162 		return 0;
163 	}
164 
165 	/* do some path mangling */
166 	strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
167 	StripExtension( source );
168 	DefaultExtension( source, ".bsp" );
169 
170 	/* load the bsp */
171 	Sys_Printf( "Loading %s\n", source );
172 	LoadBSPFile( source );
173 
174 	/* export the lightmaps */
175 	ExportLightmaps();
176 
177 	/* return to sender */
178 	return 0;
179 }
180 
181 
182 
183 /*
184 ImportLightmapsMain()
185 imports the lightmaps from a list of numbered tga images
186 */
187 
ImportLightmapsMain(int argc,char ** argv)188 int ImportLightmapsMain( int argc, char **argv )
189 {
190 	int			i, x, y, len, width, height;
191 	char		dirname[ 1024 ], filename[ 1024 ];
192 	byte		*lightmap, *buffer, *pixels, *in, *out;
193 
194 
195 	/* arg checking */
196 	if( argc < 1 )
197 	{
198 		Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
199 		return 0;
200 	}
201 
202 	/* do some path mangling */
203 	strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
204 	StripExtension( source );
205 	DefaultExtension( source, ".bsp" );
206 
207 	/* load the bsp */
208 	Sys_Printf( "Loading %s\n", source );
209 	LoadBSPFile( source );
210 
211 	/* note it */
212 	Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n");
213 
214 	/* do some path mangling */
215 	strcpy( dirname, source );
216 	StripExtension( dirname );
217 
218 	/* sanity check */
219 	if( bspLightBytes == NULL )
220 		Error( "No lightmap data" );
221 
222 	/* make a directory for the lightmaps */
223 	Q_mkdir( dirname );
224 
225 	/* iterate through the lightmaps */
226 	for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (game->lightmapSize * game->lightmapSize * 3) )
227 	{
228 		/* read a tga image */
229 		sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
230 		Sys_Printf( "Loading %s\n", filename );
231 		buffer = NULL;
232 		len = vfsLoadFile( filename, (void*) &buffer, -1 );
233 		if( len < 0 )
234 		{
235 			Sys_Printf( "WARNING: Unable to load image %s\n", filename );
236 			continue;
237 		}
238 
239 		/* parse file into an image */
240 		pixels = NULL;
241 		LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
242 		free( buffer );
243 
244 		/* sanity check it */
245 		if( pixels == NULL )
246 		{
247 			Sys_Printf( "WARNING: Unable to load image %s\n", filename );
248 			continue;
249 		}
250 		if( width != game->lightmapSize || height != game->lightmapSize )
251 			Sys_Printf( "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
252 				filename, width, height, game->lightmapSize, game->lightmapSize );
253 
254 		/* copy the pixels */
255 		in = pixels;
256 		for( y = 1; y <= game->lightmapSize; y++ )
257 		{
258 			out = lightmap + ((game->lightmapSize - y) * game->lightmapSize * 3);
259 			for( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
260 				VectorCopy( in, out );
261 		}
262 
263 		/* free the image */
264 		free( pixels );
265 	}
266 
267 	/* write the bsp */
268 	Sys_Printf( "writing %s\n", source );
269 	WriteBSPFile( source );
270 
271 	/* return to sender */
272 	return 0;
273 }
274 
275 
276 
277 /* -------------------------------------------------------------------------------
278 
279 this section deals with projecting a lightmap onto a raw drawsurface
280 
281 ------------------------------------------------------------------------------- */
282 
283 /*
284 CompareLightSurface()
285 compare function for qsort()
286 */
287 
CompareLightSurface(const void * a,const void * b)288 static int CompareLightSurface( const void *a, const void *b )
289 {
290 	shaderInfo_t	*asi, *bsi;
291 
292 
293 	/* get shaders */
294 	asi = surfaceInfos[ *((int*) a) ].si;
295 	bsi = surfaceInfos[ *((int*) b) ].si;
296 
297 	/* dummy check */
298 	if( asi == NULL )
299 		return -1;
300 	if( bsi == NULL )
301 		return 1;
302 
303 	/* compare shader names */
304 	return strcmp( asi->shader, bsi->shader );
305 }
306 
307 
308 
309 /*
310 FinishRawLightmap()
311 allocates a raw lightmap's necessary buffers
312 */
313 
FinishRawLightmap(rawLightmap_t * lm)314 void FinishRawLightmap( rawLightmap_t *lm )
315 {
316 	int					i, j, c, size, *sc;
317 	float				is;
318 	surfaceInfo_t		*info;
319 
320 
321 	/* sort light surfaces by shader name */
322 	qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
323 
324 	/* count clusters */
325 	lm->numLightClusters = 0;
326 	for( i = 0; i < lm->numLightSurfaces; i++ )
327 	{
328 		/* get surface info */
329 		info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
330 
331 		/* add surface clusters */
332 		lm->numLightClusters += info->numSurfaceClusters;
333 	}
334 
335 	/* allocate buffer for clusters and copy */
336 	lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
337 	c = 0;
338 	for( i = 0; i < lm->numLightSurfaces; i++ )
339 	{
340 		/* get surface info */
341 		info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
342 
343 		/* add surface clusters */
344 		for( j = 0; j < info->numSurfaceClusters; j++ )
345 			lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
346 	}
347 
348 	/* set styles */
349 	lm->styles[ 0 ] = LS_NORMAL;
350 	for( i = 1; i < MAX_LIGHTMAPS; i++ )
351 		lm->styles[ i ] = LS_NONE;
352 
353 	/* set supersampling size */
354 	lm->sw = lm->w * superSample;
355 	lm->sh = lm->h * superSample;
356 
357 	/* add to super luxel count */
358 	numRawSuperLuxels += (lm->sw * lm->sh);
359 
360 	/* manipulate origin/vecs for supersampling */
361 	if( superSample > 1 && lm->vecs != NULL )
362 	{
363 		/* calc inverse supersample */
364 		is = 1.0f / superSample;
365 
366 		/* scale the vectors and shift the origin */
367 		#if 1
368 			/* new code that works for arbitrary supersampling values */
369 			VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
370 			VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
371 			VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
372 			VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
373 			VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
374 			VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
375 		#else
376 			/* old code that only worked with a value of 2 */
377 			VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
378 			VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
379 			VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
380 			VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
381 		#endif
382 	}
383 
384 	/* allocate bsp lightmap storage */
385 	size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
386 	if( lm->bspLuxels[ 0 ] == NULL )
387 		lm->bspLuxels[ 0 ] = safe_malloc( size );
388 	memset( lm->bspLuxels[ 0 ], 0, size );
389 
390 	/* allocate radiosity lightmap storage */
391 	if( bounce )
392 	{
393 		size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
394 		if( lm->radLuxels[ 0 ] == NULL )
395 			lm->radLuxels[ 0 ] = safe_malloc( size );
396 		memset( lm->radLuxels[ 0 ], 0, size );
397 	}
398 
399 	/* allocate sampling lightmap storage */
400 	size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
401 	if( lm->superLuxels[ 0 ] == NULL )
402 		lm->superLuxels[ 0 ] = safe_malloc( size );
403 	memset( lm->superLuxels[ 0 ], 0, size );
404 
405 	/* allocate origin map storage */
406 	size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
407 	if( lm->superOrigins == NULL )
408 		lm->superOrigins = safe_malloc( size );
409 	memset( lm->superOrigins, 0, size );
410 
411 	/* allocate normal map storage */
412 	size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
413 	if( lm->superNormals == NULL )
414 		lm->superNormals = safe_malloc( size );
415 	memset( lm->superNormals, 0, size );
416 
417 	/* allocate cluster map storage */
418 	size = lm->sw * lm->sh * sizeof( int );
419 	if( lm->superClusters == NULL )
420 		lm->superClusters = safe_malloc( size );
421 	size = lm->sw * lm->sh;
422 	sc = lm->superClusters;
423 	for( i = 0; i < size; i++ )
424 		(*sc++) = CLUSTER_UNMAPPED;
425 
426 	/* deluxemap allocation */
427 	if( deluxemap )
428 	{
429 		/* allocate sampling deluxel storage */
430 		size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
431 		if( lm->superDeluxels == NULL )
432 			lm->superDeluxels = safe_malloc( size );
433 		memset( lm->superDeluxels, 0, size );
434 
435 		/* allocate bsp deluxel storage */
436 		size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
437 		if( lm->bspDeluxels == NULL )
438 			lm->bspDeluxels = safe_malloc( size );
439 		memset( lm->bspDeluxels, 0, size );
440 	}
441 
442 	/* add to count */
443 	numLuxels += (lm->sw * lm->sh);
444 }
445 
446 
447 
448 /*
449 AddPatchToRawLightmap()
450 projects a lightmap for a patch surface
451 since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
452 it is no longer necessary for patch verts to fall exactly on a lightmap sample
453 based on AllocateLightmapForPatch()
454 */
455 
AddPatchToRawLightmap(int num,rawLightmap_t * lm)456 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm )
457 {
458 	bspDrawSurface_t	*ds;
459 	surfaceInfo_t		*info;
460 	int					x, y;
461 	bspDrawVert_t		*verts, *a, *b;
462 	vec3_t				delta;
463 	mesh_t				src, *subdivided, *mesh;
464 	float				sBasis, tBasis, s, t;
465 	float				length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
466 
467 
468 	/* patches finish a raw lightmap */
469 	lm->finished = qtrue;
470 
471 	/* get surface and info  */
472 	ds = &bspDrawSurfaces[ num ];
473 	info = &surfaceInfos[ num ];
474 
475 	/* make a temporary mesh from the drawsurf */
476 	src.width = ds->patchWidth;
477 	src.height = ds->patchHeight;
478 	src.verts = &yDrawVerts[ ds->firstVert ];
479 	//%	subdivided = SubdivideMesh( src, 8, 512 );
480 	subdivided = SubdivideMesh2( src, info->patchIterations );
481 
482 	/* fit it to the curve and remove colinear verts on rows/columns */
483 	PutMeshOnCurve( *subdivided );
484 	mesh = RemoveLinearMeshColumnsRows( subdivided );
485 	FreeMesh( subdivided );
486 
487 	/* find the longest distance on each row/column */
488 	verts = mesh->verts;
489 	memset( widthTable, 0, sizeof( widthTable ) );
490 	memset( heightTable, 0, sizeof( heightTable ) );
491 	for( y = 0; y < mesh->height; y++ )
492 	{
493 		for( x = 0; x < mesh->width; x++ )
494 		{
495 			/* get width */
496 			if( x + 1 < mesh->width )
497 			{
498 				a = &verts[ (y * mesh->width) + x ];
499 				b = &verts[ (y * mesh->width) + x + 1 ];
500 				VectorSubtract( a->xyz, b->xyz, delta );
501 				length = VectorLength( delta );
502 				if( length > widthTable[ x ] )
503 					widthTable[ x ] = length;
504 			}
505 
506 			/* get height */
507 			if( y + 1 < mesh->height )
508 			{
509 				a = &verts[ (y * mesh->width) + x ];
510 				b = &verts[ ((y + 1) * mesh->width) + x ];
511 				VectorSubtract( a->xyz, b->xyz, delta );
512 				length = VectorLength( delta );
513 				if( length > heightTable[ y ] )
514 					heightTable[ y ] = length;
515 			}
516 		}
517 	}
518 
519 	/* determine lightmap width */
520 	length = 0;
521 	for( x = 0; x < (mesh->width - 1); x++ )
522 		length += widthTable[ x ];
523 	lm->w = ceil( length / lm->sampleSize ) + 1;
524 	if( lm->w < ds->patchWidth )
525 		lm->w = ds->patchWidth;
526 	if( lm->w > lm->customWidth )
527 		lm->w = lm->customWidth;
528 	sBasis = (float) (lm->w - 1) / (float) (ds->patchWidth - 1);
529 
530 	/* determine lightmap height */
531 	length = 0;
532 	for( y = 0; y < (mesh->height - 1); y++ )
533 		length += heightTable[ y ];
534 	lm->h = ceil( length / lm->sampleSize ) + 1;
535 	if( lm->h < ds->patchHeight )
536 		lm->h = ds->patchHeight;
537 	if( lm->h > lm->customHeight )
538 		lm->h = lm->customHeight;
539 	tBasis = (float) (lm->h - 1) / (float) (ds->patchHeight - 1);
540 
541 	/* free the temporary mesh */
542 	FreeMesh( mesh );
543 
544 	/* set the lightmap texture coordinates in yDrawVerts */
545 	lm->wrap[ 0 ] = qtrue;
546 	lm->wrap[ 1 ] = qtrue;
547 	verts = &yDrawVerts[ ds->firstVert ];
548 	for( y = 0; y < ds->patchHeight; y++ )
549 	{
550 		t = (tBasis * y) + 0.5f;
551 		for( x = 0; x < ds->patchWidth; x++ )
552 		{
553 			s = (sBasis * x) + 0.5f;
554 			verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
555 			verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
556 
557 			if( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ((ds->patchHeight - 1) * ds->patchWidth) + x ].xyz ) )
558 				lm->wrap[ 1 ] = qfalse;
559 		}
560 
561 		if( !VectorCompare( verts[ (y * ds->patchWidth) ].xyz, verts[ (y * ds->patchWidth) + (ds->patchWidth - 1) ].xyz ) )
562 			lm->wrap[ 0 ] = qfalse;
563 	}
564 
565 	/* debug code: */
566 	//%	Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
567 	//% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
568 	//%		Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
569 	//% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
570 	//% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
571 
572 	/* add to counts */
573 	numPatchesLightmapped++;
574 
575 	/* return */
576 	return qtrue;
577 }
578 
579 
580 
581 /*
582 AddSurfaceToRawLightmap()
583 projects a lightmap for a surface
584 based on AllocateLightmapForSurface()
585 */
586 
AddSurfaceToRawLightmap(int num,rawLightmap_t * lm)587 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm )
588 {
589 	bspDrawSurface_t	*ds, *ds2;
590 	surfaceInfo_t		*info, *info2;
591 	int					num2, n, i, axisNum;
592 	float				s, t, d, len, sampleSize;
593 	vec3_t				mins, maxs, origin, faxis, size, exactSize, delta, normalized, vecs[ 2 ];
594 	vec4_t				plane;
595 	bspDrawVert_t		*verts;
596 
597 
598 	/* get surface and info  */
599 	ds = &bspDrawSurfaces[ num ];
600 	info = &surfaceInfos[ num ];
601 
602 	/* add the surface to the raw lightmap */
603 	lightSurfaces[ numLightSurfaces++ ] = num;
604 	lm->numLightSurfaces++;
605 
606 	/* does this raw lightmap already have any surfaces? */
607 	if( lm->numLightSurfaces > 1 )
608 	{
609 		/* surface and raw lightmap must have the same lightmap projection axis */
610 		if( VectorCompare( info->axis, lm->axis ) == qfalse )
611 			return qfalse;
612 
613 		/* match identical attributes */
614 		if( info->sampleSize != lm->sampleSize ||
615 			info->entityNum != lm->entityNum ||
616 			info->recvShadows != lm->recvShadows ||
617 			info->si->lmCustomWidth != lm->customWidth ||
618 			info->si->lmCustomHeight != lm->customHeight ||
619 			info->si->lmBrightness != lm->brightness ||
620 			info->si->lmFilterRadius != lm->filterRadius ||
621 			info->si->splotchFix != lm->splotchFix )
622 			return qfalse;
623 
624 		/* surface bounds must intersect with raw lightmap bounds */
625 		for( i = 0; i < 3; i++ )
626 		{
627 			if( info->mins[ i ] > lm->maxs[ i ] )
628 				return qfalse;
629 			if( info->maxs[ i ] < lm->mins[ i ] )
630 				return qfalse;
631 		}
632 
633 		/* plane check (fixme: allow merging of nonplanars) */
634 		if( info->si->lmMergable == qfalse )
635 		{
636 			if( info->plane == NULL || lm->plane == NULL )
637 				return qfalse;
638 
639 			/* compare planes */
640 			for( i = 0; i < 4; i++ )
641 				if( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON )
642 					return qfalse;
643 		}
644 
645 		/* debug code hacking */
646 		//%	if( lm->numLightSurfaces > 1 )
647 		//%		return qfalse;
648 	}
649 
650 	/* set plane */
651 	if( info->plane == NULL )
652 		lm->plane = NULL;
653 
654 	/* add surface to lightmap bounds */
655 	AddPointToBounds( info->mins, lm->mins, lm->maxs );
656 	AddPointToBounds( info->maxs, lm->mins, lm->maxs );
657 
658 	/* check to see if this is a non-planar patch */
659 	if( ds->surfaceType == MST_PATCH &&
660 		lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f )
661 		return AddPatchToRawLightmap( num, lm );
662 
663 	/* start with initially requested sample size */
664 	sampleSize = lm->sampleSize;
665 
666 	/* round to the lightmap resolution */
667 	for( i = 0; i < 3; i++ )
668 	{
669 		exactSize[ i ] = lm->maxs[ i ] - lm->mins[ i ];
670  		mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
671  		maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
672  		size[ i ] = (maxs[ i ] - mins[ i ]) / sampleSize + 1.0f;
673 
674 		/* hack (god this sucks) */
675 		if( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight )
676 		{
677 			i = -1;
678 			sampleSize += 1.0f;
679 		}
680 	}
681 
682 	/* set actual sample size */
683 	lm->actualSampleSize = sampleSize;
684 
685 	/* fixme: copy rounded mins/maxes to lightmap record? */
686 	if( lm->plane == NULL )
687 	{
688 		VectorCopy( mins, lm->mins );
689 		VectorCopy( maxs, lm->maxs );
690 		VectorCopy( mins, origin );
691 	}
692 
693 	/* set lightmap origin */
694 	VectorCopy( lm->mins, origin );
695 
696 	/* make absolute axis */
697 	faxis[ 0 ] = fabs( lm->axis[ 0 ] );
698 	faxis[ 1 ] = fabs( lm->axis[ 1 ] );
699 	faxis[ 2 ] = fabs( lm->axis[ 2 ] );
700 
701 	/* clear out lightmap vectors */
702 	memset( vecs, 0, sizeof( vecs ) );
703 
704 	/* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
705 	if( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] )
706 	{
707 		axisNum = 2;
708 		lm->w = size[ 0 ];
709 		lm->h = size[ 1 ];
710 		vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
711 		vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
712 	}
713 	else if( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] )
714 	{
715 		axisNum = 0;
716 		lm->w = size[ 1 ];
717 		lm->h = size[ 2 ];
718 		vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
719 		vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
720 	}
721 	else
722 	{
723 		axisNum = 1;
724 		lm->w = size[ 0 ];
725 		lm->h = size[ 2 ];
726 		vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
727 		vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
728 	}
729 
730 	/* check for bogus axis */
731 	if( faxis[ axisNum ] == 0.0f )
732 	{
733 		Sys_Printf( "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
734 		lm->w = lm->h = 0;
735 		return qfalse;
736 	}
737 
738 	/* store the axis number in the lightmap */
739 	lm->axisNum = axisNum;
740 
741 	/* walk the list of surfaces on this raw lightmap */
742 	for( n = 0; n < lm->numLightSurfaces; n++ )
743 	{
744 		/* get surface */
745 		num2 = lightSurfaces[ lm->firstLightSurface + n ];
746 		ds2 = &bspDrawSurfaces[ num2 ];
747 		info2 = &surfaceInfos[ num2 ];
748 		verts = &yDrawVerts[ ds2->firstVert ];
749 
750 		/* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
751 		for( i = 0; i < ds2->numVerts; i++ )
752 		{
753 			VectorSubtract( verts[ i ].xyz, origin, delta );
754 			s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
755 			t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
756 			verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
757 			verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
758 
759 			if( s > (float) lm->w || t > (float) lm->h )
760 			{
761 				Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
762 					s, lm->w, t, lm->h );
763 			}
764 		}
765 	}
766 
767 	/* get first drawsurface */
768 	num2 = lightSurfaces[ lm->firstLightSurface ];
769 	ds2 = &bspDrawSurfaces[ num2 ];
770 	info2 = &surfaceInfos[ num2 ];
771 	verts = &yDrawVerts[ ds2->firstVert ];
772 
773 	/* calculate lightmap origin */
774 	if( VectorLength( ds2->lightmapVecs[ 2 ] ) )
775 		VectorCopy( ds2->lightmapVecs[ 2 ], plane );
776 	else
777 		VectorCopy( lm->axis, plane );
778 	plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
779 
780 	VectorCopy( origin, lm->origin );
781 	d = DotProduct( lm->origin, plane ) - plane[ 3 ];
782 	d /= plane[ axisNum ];
783 	lm->origin[ axisNum ] -= d;
784 
785 	/* legacy support */
786 	VectorCopy( lm->origin, ds->lightmapOrigin );
787 
788 	/* for planar surfaces, create lightmap vectors for st->xyz conversion */
789 	if( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 )	/* ydnar: can't remember what exactly i was thinking here... */
790 	{
791 		/* allocate space for the vectors */
792 		lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
793 		memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
794 		VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
795 
796 		/* project stepped lightmap blocks and subtract to get planevecs */
797 		for( i = 0; i < 2; i++ )
798 		{
799 			len = VectorNormalize( vecs[ i ], normalized );
800 			VectorScale( normalized, (1.0 / len), lm->vecs[ i ] );
801 			d = DotProduct( lm->vecs[ i ], plane );
802 			d /= plane[ axisNum ];
803 			lm->vecs[ i ][ axisNum ] -= d;
804 		}
805 	}
806 	else
807 	{
808 		/* lightmap vectors are useless on a non-planar surface */
809 		lm->vecs = NULL;
810 	}
811 
812 	/* add to counts */
813 	if( ds->surfaceType == MST_PATCH )
814 	{
815 		numPatchesLightmapped++;
816 		if( lm->plane != NULL )
817 			numPlanarPatchesLightmapped++;
818 	}
819 	else
820 	{
821 		if( lm->plane != NULL )
822 			numPlanarsLightmapped++;
823 		else
824 			numNonPlanarsLightmapped++;
825 	}
826 
827 	/* return */
828 	return qtrue;
829 }
830 
831 
832 
833 /*
834 CompareSurfaceInfo()
835 compare function for qsort()
836 */
837 
CompareSurfaceInfo(const void * a,const void * b)838 static int CompareSurfaceInfo( const void *a, const void *b )
839 {
840 	surfaceInfo_t	*aInfo, *bInfo;
841 	int				i;
842 
843 
844 	/* get surface info */
845 	aInfo = &surfaceInfos[ *((int*) a) ];
846 	bInfo = &surfaceInfos[ *((int*) b) ];
847 
848 	/* model first */
849 	if( aInfo->model < bInfo->model )
850 		return 1;
851 	else if( aInfo->model > bInfo->model )
852 		return -1;
853 
854 	/* then lightmap status */
855 	if( aInfo->hasLightmap < bInfo->hasLightmap )
856 		return 1;
857 	else if( aInfo->hasLightmap > bInfo->hasLightmap )
858 		return -1;
859 
860 	/* then lightmap sample size */
861 	if( aInfo->sampleSize < bInfo->sampleSize )
862 		return 1;
863 	else if( aInfo->sampleSize > bInfo->sampleSize )
864 		return -1;
865 
866 	/* then lightmap axis */
867 	for( i = 0; i < 3; i++ )
868 	{
869 		if( aInfo->axis[ i ] < bInfo->axis[ i ] )
870 			return 1;
871 		else if( aInfo->axis[ i ] > bInfo->axis[ i ] )
872 			return -1;
873 	}
874 
875 	/* then plane */
876 	if( aInfo->plane == NULL && bInfo->plane != NULL )
877 		return 1;
878 	else if( aInfo->plane != NULL && bInfo->plane == NULL )
879 		return -1;
880 	else if( aInfo->plane != NULL && bInfo->plane != NULL )
881 	{
882 		for( i = 0; i < 4; i++ )
883 		{
884 			if( aInfo->plane[ i ] < bInfo->plane[ i ] )
885 				return 1;
886 			else if( aInfo->plane[ i ] > bInfo->plane[ i ] )
887 				return -1;
888 		}
889 	}
890 
891 	/* then position in world */
892 	for( i = 0; i < 3; i++ )
893 	{
894 		if( aInfo->mins[ i ] < bInfo->mins[ i ] )
895 			return 1;
896 		else if( aInfo->mins[ i ] > bInfo->mins[ i ] )
897 			return -1;
898 	}
899 
900 	/* these are functionally identical (this should almost never happen) */
901 	return 0;
902 }
903 
904 
905 
906 /*
907 SetupSurfaceLightmaps()
908 allocates lightmaps for every surface in the bsp that needs one
909 this depends on yDrawVerts being allocated
910 */
911 
SetupSurfaceLightmaps(void)912 void SetupSurfaceLightmaps( void )
913 {
914 	int					i, j, k, s,num, num2;
915 	bspModel_t			*model;
916 	bspLeaf_t			*leaf;
917 	bspDrawSurface_t	*ds, *ds2;
918 	surfaceInfo_t		*info, *info2;
919 	rawLightmap_t		*lm;
920 	qboolean			added;
921 	vec3_t				mapSize, entityOrigin;
922 
923 
924 	/* note it */
925 	Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n");
926 
927 	/* determine supersample amount */
928 	if( superSample < 1 )
929 		superSample = 1;
930 	else if( superSample > 8 )
931 	{
932 		Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
933 		superSample = 8;
934 	}
935 
936 	/* clear map bounds */
937 	ClearBounds( mapMins, mapMaxs );
938 
939 	/* allocate a list of surface clusters */
940 	numSurfaceClusters = 0;
941 	maxSurfaceClusters = numBSPLeafSurfaces;
942 	surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
943 	memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
944 
945 	/* allocate a list for per-surface info */
946 	surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
947 	memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
948 	for( i = 0; i < numBSPDrawSurfaces; i++ )
949 		surfaceInfos[ i ].childSurfaceNum = -1;
950 
951 	/* allocate a list of surface indexes to be sorted */
952 	sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
953 	memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
954 
955 	/* walk each model in the bsp */
956 	for( i = 0; i < numBSPModels; i++ )
957 	{
958 		/* get model */
959 		model = &bspModels[ i ];
960 
961 		/* walk the list of surfaces in this model and fill out the info structs */
962 		for( j = 0; j < model->numBSPSurfaces; j++ )
963 		{
964 			/* make surface index */
965 			num = model->firstBSPSurface + j;
966 
967 			/* copy index to sort list */
968 			sortSurfaces[ num ] = num;
969 
970 			/* get surface and info */
971 			ds = &bspDrawSurfaces[ num ];
972 			info = &surfaceInfos[ num ];
973 
974 			/* set entity origin */
975 			if( ds->numVerts > 0 )
976 				VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
977 			else
978 				VectorClear( entityOrigin );
979 
980 			/* basic setup */
981 			info->model = model;
982 			info->lm = NULL;
983 			info->plane = NULL;
984 			info->firstSurfaceCluster = numSurfaceClusters;
985 
986 			/* get extra data */
987 			info->si = GetSurfaceExtraShaderInfo( num );
988 			if( info->si == NULL )
989 				info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
990 			info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
991 			info->entityNum = GetSurfaceExtraEntityNum( num );
992 			info->castShadows = GetSurfaceExtraCastShadows( num );
993 			info->recvShadows = GetSurfaceExtraRecvShadows( num );
994 			info->sampleSize = GetSurfaceExtraSampleSize( num );
995 			info->longestCurve = GetSurfaceExtraLongestCurve( num );
996 			info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
997 			GetSurfaceExtraLightmapAxis( num, info->axis );
998 
999 			/* mark parent */
1000 			if( info->parentSurfaceNum >= 0 )
1001 				surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1002 
1003 			/* determine surface bounds */
1004 			ClearBounds( info->mins, info->maxs );
1005 			for( k = 0; k < ds->numVerts; k++ )
1006 			{
1007 				AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1008 				AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1009 			}
1010 
1011 			/* find all the bsp clusters the surface falls into */
1012 			for( k = 0; k < numBSPLeafs; k++ )
1013 			{
1014 				/* get leaf */
1015 				leaf = &bspLeafs[ k ];
1016 
1017 				/* test bbox */
1018 				if( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1019 					leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1020 					leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] )
1021 					continue;
1022 
1023 				/* test leaf surfaces */
1024 				for( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1025 				{
1026 					if( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num )
1027 					{
1028 						if( numSurfaceClusters >= maxSurfaceClusters )
1029 							Error( "maxSurfaceClusters exceeded" );
1030 						surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1031 						numSurfaceClusters++;
1032 						info->numSurfaceClusters++;
1033 					}
1034 				}
1035 			}
1036 
1037 			/* determine if surface is planar */
1038 			if( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f )
1039 			{
1040 				/* make a plane */
1041 				info->plane = safe_malloc( 4 * sizeof( float ) );
1042 				VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1043 				info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1044 			}
1045 
1046 			/* determine if surface requires a lightmap */
1047 			if( ds->surfaceType == MST_TRIANGLE_SOUP ||
1048 				ds->surfaceType == MST_FOLIAGE ||
1049 				(info->si->compileFlags & C_VERTEXLIT) )
1050 				numSurfsVertexLit++;
1051 			else
1052 			{
1053 				numSurfsLightmapped++;
1054 				info->hasLightmap = qtrue;
1055 			}
1056 		}
1057 	}
1058 
1059 	/* find longest map distance */
1060 	VectorSubtract( mapMaxs, mapMins, mapSize );
1061 	maxMapDistance = VectorLength( mapSize );
1062 
1063 	/* sort the surfaces info list */
1064 	qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1065 
1066 	/* allocate a list of surfaces that would go into raw lightmaps */
1067 	numLightSurfaces = 0;
1068 	lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1069 	memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1070 
1071 	/* allocate a list of raw lightmaps */
1072 	numRawSuperLuxels = 0;
1073 	numRawLightmaps = 0;
1074 	rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1075 	memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1076 
1077 	/* walk the list of sorted surfaces */
1078 	for( i = 0; i < numBSPDrawSurfaces; i++ )
1079 	{
1080 		/* get info and attempt early out */
1081 		num = sortSurfaces[ i ];
1082 		ds = &bspDrawSurfaces[ num ];
1083 		info = &surfaceInfos[ num ];
1084 		if( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 )
1085 			continue;
1086 
1087 		/* allocate a new raw lightmap */
1088 		lm = &rawLightmaps[ numRawLightmaps ];
1089 		numRawLightmaps++;
1090 
1091 		/* set it up */
1092 		lm->splotchFix = info->si->splotchFix;
1093 		lm->firstLightSurface = numLightSurfaces;
1094 		lm->numLightSurfaces = 0;
1095 		lm->sampleSize = info->sampleSize;
1096 		lm->actualSampleSize = info->sampleSize;
1097 		lm->entityNum = info->entityNum;
1098 		lm->recvShadows = info->recvShadows;
1099 		lm->brightness = info->si->lmBrightness;
1100 		lm->filterRadius = info->si->lmFilterRadius;
1101 		VectorCopy( info->axis, lm->axis );
1102 		lm->plane = info->plane;
1103 		VectorCopy( info->mins, lm->mins );
1104 		VectorCopy( info->maxs, lm->maxs );
1105 
1106 		lm->customWidth = info->si->lmCustomWidth;
1107 		lm->customHeight = info->si->lmCustomHeight;
1108 
1109 		/* add the surface to the raw lightmap */
1110 		AddSurfaceToRawLightmap( num, lm );
1111 		info->lm = lm;
1112 
1113 		/* do an exhaustive merge */
1114 		added = qtrue;
1115 		while( added )
1116 		{
1117 			/* walk the list of surfaces again */
1118 			added = qfalse;
1119 			for( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1120 			{
1121 				/* get info and attempt early out */
1122 				num2 = sortSurfaces[ j ];
1123 				ds2 = &bspDrawSurfaces[ num2 ];
1124 				info2 = &surfaceInfos[ num2 ];
1125 				if( info2->hasLightmap == qfalse || info2->lm != NULL )
1126 					continue;
1127 
1128 				/* add the surface to the raw lightmap */
1129 				if( AddSurfaceToRawLightmap( num2, lm ) )
1130 				{
1131 					info2->lm = lm;
1132 					added = qtrue;
1133 				}
1134 				else
1135 				{
1136 					/* back up one */
1137 					lm->numLightSurfaces--;
1138 					numLightSurfaces--;
1139 				}
1140 			}
1141 		}
1142 
1143 		/* finish the lightmap and allocate the various buffers */
1144 		FinishRawLightmap( lm );
1145 	}
1146 
1147 	/* allocate vertex luxel storage */
1148 	for( k = 0; k < MAX_LIGHTMAPS; k++ )
1149 	{
1150 		vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1151 		memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1152 		radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1153 		memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1154 	}
1155 
1156 	/* emit some stats */
1157 	Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1158 	Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1159 	Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1160 	Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1161 	Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1162 	Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1163 	Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1164 	Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1165 }
1166 
1167 
1168 
1169 /*
1170 StitchSurfaceLightmaps()
1171 stitches lightmap edges
1172 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1173 */
1174 
1175 #define MAX_STITCH_CANDIDATES	32
1176 #define MAX_STITCH_LUXELS		64
1177 
StitchSurfaceLightmaps(void)1178 void StitchSurfaceLightmaps( void )
1179 {
1180 	int				i, j, x, y, x2, y2, *cluster, *cluster2,
1181 					numStitched, numCandidates, numLuxels, f, fOld, start;
1182 	rawLightmap_t	*lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1183 	float			*luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1184 					sampleSize, average[ 3 ], totalColor, ootc, *luxels[ MAX_STITCH_LUXELS ];
1185 
1186 
1187 	/* disabled for now */
1188 	return;
1189 
1190 	/* note it */
1191 	Sys_Printf( "--- StitchSurfaceLightmaps ---\n");
1192 
1193 	/* init pacifier */
1194 	fOld = -1;
1195 	start = I_FloatTime();
1196 
1197 	/* walk the list of raw lightmaps */
1198 	numStitched = 0;
1199 	for( i = 0; i < numRawLightmaps; i++ )
1200 	{
1201 		/* print pacifier */
1202 		f = 10 * i / numRawLightmaps;
1203 		if( f != fOld )
1204 		{
1205 			fOld = f;
1206 			Sys_Printf( "%i...", f );
1207 		}
1208 
1209 		/* get lightmap a */
1210 		a = &rawLightmaps[ i ];
1211 
1212 		/* walk rest of lightmaps */
1213 		numCandidates = 0;
1214 		for( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1215 		{
1216 			/* get lightmap b */
1217 			b = &rawLightmaps[ j ];
1218 
1219 			/* test bounding box */
1220 			if( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1221 				a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1222 				a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] )
1223 				continue;
1224 
1225 			/* add candidate */
1226 			c[ numCandidates++ ] = b;
1227 		}
1228 
1229 		/* walk luxels */
1230 		for( y = 0; y < a->sh; y++ )
1231 		{
1232 			for( x = 0; x < a->sw; x++ )
1233 			{
1234 				/* ignore unmapped/unlit luxels */
1235 				lm = a;
1236 				cluster = SUPER_CLUSTER( x, y );
1237 				if( *cluster == CLUSTER_UNMAPPED )
1238 					continue;
1239 				luxel = SUPER_LUXEL( 0, x, y );
1240 				if( luxel[ 3 ] <= 0.0f )
1241 					continue;
1242 
1243 				/* get particulars */
1244 				origin = SUPER_ORIGIN( x, y );
1245 				normal = SUPER_NORMAL( x, y );
1246 
1247 				/* walk candidate list */
1248 				for( j = 0; j < numCandidates; j++ )
1249 				{
1250 					/* get candidate */
1251 					b = c[ j ];
1252 					lm = b;
1253 
1254 					/* set samplesize to the smaller of the pair */
1255 					sampleSize = 0.5f * (a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize);
1256 
1257 					/* test bounding box */
1258 					if( origin[ 0 ] < (b->mins[ 0 ] - sampleSize) || (origin[ 0 ] > b->maxs[ 0 ] + sampleSize) ||
1259 						origin[ 1 ] < (b->mins[ 1 ] - sampleSize) || (origin[ 1 ] > b->maxs[ 1 ] + sampleSize) ||
1260 						origin[ 2 ] < (b->mins[ 2 ] - sampleSize) || (origin[ 2 ] > b->maxs[ 2 ] + sampleSize) )
1261 						continue;
1262 
1263 					/* walk candidate luxels */
1264 					VectorClear( average );
1265 					numLuxels = 0;
1266 					totalColor = 0.0f;
1267 					for( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1268 					{
1269 						for( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1270 						{
1271 							/* ignore same luxels */
1272 							if( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 )
1273 								continue;
1274 
1275 							/* ignore unmapped/unlit luxels */
1276 							cluster2 = SUPER_CLUSTER( x2, y2 );
1277 							if( *cluster2 == CLUSTER_UNMAPPED )
1278 								continue;
1279 							luxel2 = SUPER_LUXEL( 0, x2, y2 );
1280 							if( luxel2[ 3 ] <= 0.0f )
1281 								continue;
1282 
1283 							/* get particulars */
1284 							origin2 = SUPER_ORIGIN( x2, y2 );
1285 							normal2 = SUPER_NORMAL( x2, y2 );
1286 
1287 							/* test normal */
1288 							if( DotProduct( normal, normal2 ) < 0.5f )
1289 								continue;
1290 
1291 							/* test bounds */
1292 							if( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1293 								fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1294 								fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize )
1295 								continue;
1296 
1297 							/* add luxel */
1298 							//%	VectorSet( luxel2, 255, 0, 255 );
1299 							luxels[ numLuxels++ ] = luxel2;
1300 							VectorAdd( average, luxel2, average );
1301 							totalColor += luxel2[ 3 ];
1302 						}
1303 					}
1304 
1305 					/* early out */
1306 					if( numLuxels == 0 )
1307 						continue;
1308 
1309 					/* scale average */
1310 					ootc = 1.0f / totalColor;
1311 					VectorScale( average, ootc, luxel );
1312 					luxel[ 3 ] = 1.0f;
1313 					numStitched++;
1314 				}
1315 			}
1316 		}
1317 	}
1318 
1319 	/* emit statistics */
1320 	Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
1321 	Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1322 }
1323 
1324 
1325 
1326 /*
1327 CompareBSPLuxels()
1328 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1329 */
1330 
1331 #define SOLID_EPSILON		0.0625
1332 #define LUXEL_TOLERANCE		0.0025
1333 #define LUXEL_COLOR_FRAC	0.001302083	/* 1 / 3 / 256 */
1334 
CompareBSPLuxels(rawLightmap_t * a,int aNum,rawLightmap_t * b,int bNum)1335 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1336 {
1337 	rawLightmap_t	*lm;
1338 	int				x, y;
1339 	double			delta, total, rd, gd, bd;
1340 	float			*aLuxel, *bLuxel;
1341 
1342 
1343 	/* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1344 	if( (minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ]) &&
1345 		((aNum == 0 && bNum != 0) || (aNum != 0 && bNum == 0)) )
1346 		return qfalse;
1347 
1348 	/* basic tests */
1349 	if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1350 		a->brightness != b->brightness ||
1351 		a->solid[ aNum ] != b->solid[ bNum ] ||
1352 		a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1353 		return qfalse;
1354 
1355 	/* compare solid color lightmaps */
1356 	if( a->solid[ aNum ] && b->solid[ bNum ] )
1357 	{
1358 		/* get deltas */
1359 		rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1360 		gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1361 		bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1362 
1363 		/* compare color */
1364 		if( rd > SOLID_EPSILON || gd > SOLID_EPSILON|| bd > SOLID_EPSILON )
1365 			return qfalse;
1366 
1367 		/* okay */
1368 		return qtrue;
1369 	}
1370 
1371 	/* compare nonsolid lightmaps */
1372 	if( a->w != b->w || a->h != b->h )
1373 		return qfalse;
1374 
1375 	/* compare luxels */
1376 	delta = 0.0;
1377 	total = 0.0;
1378 	for( y = 0; y < a->h; y++ )
1379 	{
1380 		for( x = 0; x < a->w; x++ )
1381 		{
1382 			/* increment total */
1383 			total += 1.0;
1384 
1385 			/* get luxels */
1386 			lm = a;	aLuxel = BSP_LUXEL( aNum, x, y );
1387 			lm = b;	bLuxel = BSP_LUXEL( bNum, x, y );
1388 
1389 			/* ignore unused luxels */
1390 			if( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 )
1391 				continue;
1392 
1393 			/* get deltas */
1394 			rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1395 			gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1396 			bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1397 
1398 			/* 2003-09-27: compare individual luxels */
1399 			if( rd > 3.0 || gd > 3.0 || bd > 3.0 )
1400 				return qfalse;
1401 
1402 			/* compare (fixme: take into account perceptual differences) */
1403 			delta += rd * LUXEL_COLOR_FRAC;
1404 			delta += gd * LUXEL_COLOR_FRAC;
1405 			delta += bd * LUXEL_COLOR_FRAC;
1406 
1407 			/* is the change too high? */
1408 			if( total > 0.0 && ((delta / total) > LUXEL_TOLERANCE) )
1409 				return qfalse;
1410 		}
1411 	}
1412 
1413 	/* made it this far, they must be identical (or close enough) */
1414 	return qtrue;
1415 }
1416 
1417 
1418 
1419 /*
1420 MergeBSPLuxels()
1421 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1422 */
1423 
MergeBSPLuxels(rawLightmap_t * a,int aNum,rawLightmap_t * b,int bNum)1424 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1425 {
1426 	rawLightmap_t	*lm;
1427 	int				x, y;
1428 	float			luxel[ 3 ], *aLuxel, *bLuxel;
1429 
1430 
1431 	/* basic tests */
1432 	if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1433 		a->brightness != b->brightness ||
1434 		a->solid[ aNum ] != b->solid[ bNum ] ||
1435 		a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1436 		return qfalse;
1437 
1438 	/* compare solid lightmaps */
1439 	if( a->solid[ aNum ] && b->solid[ bNum ] )
1440 	{
1441 		/* average */
1442 		VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1443 		VectorScale( luxel, 0.5f, luxel );
1444 
1445 		/* copy to both */
1446 		VectorCopy( luxel, a->solidColor[ aNum ] );
1447 		VectorCopy( luxel, b->solidColor[ bNum ] );
1448 
1449 		/* return to sender */
1450 		return qtrue;
1451 	}
1452 
1453 	/* compare nonsolid lightmaps */
1454 	if( a->w != b->w || a->h != b->h )
1455 		return qfalse;
1456 
1457 	/* merge luxels */
1458 	for( y = 0; y < a->h; y++ )
1459 	{
1460 		for( x = 0; x < a->w; x++ )
1461 		{
1462 			/* get luxels */
1463 			lm = a;	aLuxel = BSP_LUXEL( aNum, x, y );
1464 			lm = b;	bLuxel = BSP_LUXEL( bNum, x, y );
1465 
1466 			/* handle occlusion mismatch */
1467 			if( aLuxel[ 0 ] < 0.0f )
1468 				VectorCopy( bLuxel, aLuxel );
1469 			else if( bLuxel[ 0 ] < 0.0f )
1470 				VectorCopy( aLuxel, bLuxel );
1471 			else
1472 			{
1473 				/* average */
1474 				VectorAdd( aLuxel, bLuxel, luxel );
1475 				VectorScale( luxel, 0.5f, luxel );
1476 
1477 				/* debugging code */
1478 				//%	luxel[ 2 ] += 64.0f;
1479 
1480 				/* copy to both */
1481 				VectorCopy( luxel, aLuxel );
1482 				VectorCopy( luxel, bLuxel );
1483 			}
1484 		}
1485 	}
1486 
1487 	/* done */
1488 	return qtrue;
1489 }
1490 
1491 
1492 
1493 /*
1494 ApproximateLuxel()
1495 determines if a single luxel is can be approximated with the interpolated vertex rgba
1496 */
1497 
ApproximateLuxel(rawLightmap_t * lm,bspDrawVert_t * dv)1498 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv )
1499 {
1500 	int		i, x, y, d, lightmapNum;
1501 	float	*luxel;
1502 	vec3_t	color, vertexColor;
1503 	byte	cb[ 4 ], vcb[ 4 ];
1504 
1505 
1506 	/* find luxel xy coords */
1507 	x = dv->lightmap[ 0 ][ 0 ] / superSample;
1508 	y = dv->lightmap[ 0 ][ 1 ] / superSample;
1509 	if( x < 0 )
1510 		x = 0;
1511 	else if( x >= lm->w )
1512 		x = lm->w - 1;
1513 	if( y < 0 )
1514 		y = 0;
1515 	else if( y >= lm->h )
1516 		y = lm->h - 1;
1517 
1518 	/* walk list */
1519 	for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1520 	{
1521 		/* early out */
1522 		if( lm->styles[ lightmapNum ] == LS_NONE )
1523 			continue;
1524 
1525 		/* get luxel */
1526 		luxel = BSP_LUXEL( lightmapNum, x, y );
1527 
1528 		/* ignore occluded luxels */
1529 		if( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f )
1530 			return qtrue;
1531 
1532 		/* copy, set min color and compare */
1533 		VectorCopy( luxel, color );
1534 		VectorCopy( dv->color[ 0 ], vertexColor );
1535 
1536 		/* styles are not affected by minlight */
1537 		if( lightmapNum == 0 )
1538 		{
1539 			for( i = 0; i < 3; i++ )
1540 			{
1541 				/* set min color */
1542 				if( color[ i ] < minLight[ i ] )
1543 					color[ i ] = minLight[ i ];
1544 				if( vertexColor[ i ] < minLight[ i ] )	/* note NOT minVertexLight */
1545 					vertexColor[ i ] = minLight[ i ];
1546 			}
1547 		}
1548 
1549 		/* set to bytes */
1550 		ColorToBytes( color, cb, 1.0f );
1551 		ColorToBytes( vertexColor, vcb, 1.0f );
1552 
1553 		/* compare */
1554 		for( i = 0; i < 3; i++ )
1555 		{
1556 			d = cb[ i ] - vcb[ i ];
1557 			if( d < 0 )
1558 				d *= -1;
1559 			if( d > approximateTolerance )
1560 				return qfalse;
1561 		}
1562 	}
1563 
1564 	/* close enough for the girls i date */
1565 	return qtrue;
1566 }
1567 
1568 
1569 
1570 /*
1571 ApproximateTriangle()
1572 determines if a single triangle can be approximated with vertex rgba
1573 */
1574 
ApproximateTriangle_r(rawLightmap_t * lm,bspDrawVert_t * dv[3])1575 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] )
1576 {
1577 	bspDrawVert_t	mid, *dv2[ 3 ];
1578 	int				max;
1579 
1580 
1581 	/* approximate the vertexes */
1582 	if( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse )
1583 		return qfalse;
1584 	if( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse )
1585 		return qfalse;
1586 	if( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse )
1587 		return qfalse;
1588 
1589 	/* subdivide calc */
1590 	{
1591 		int			i;
1592 		float		dx, dy, dist, maxDist;
1593 
1594 
1595 		/* find the longest edge and split it */
1596 		max = -1;
1597 		maxDist = 0;
1598 		for( i = 0; i < 3; i++ )
1599 		{
1600 			dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 0 ];
1601 			dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 1 ];
1602 			dist = sqrt( (dx * dx) + (dy * dy) );
1603 			if( dist > maxDist )
1604 			{
1605 				maxDist = dist;
1606 				max = i;
1607 			}
1608 		}
1609 
1610 		/* try to early out */
1611 		if( i < 0 || maxDist < subdivideThreshold )
1612 			return qtrue;
1613 	}
1614 
1615 	/* split the longest edge and map it */
1616 	LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
1617 	if( ApproximateLuxel( lm, &mid ) == qfalse )
1618 		return qfalse;
1619 
1620 	/* recurse to first triangle */
1621 	VectorCopy( dv, dv2 );
1622 	dv2[ max ] = &mid;
1623 	if( ApproximateTriangle_r( lm, dv2 ) == qfalse )
1624 		return qfalse;
1625 
1626 	/* recurse to second triangle */
1627 	VectorCopy( dv, dv2 );
1628 	dv2[ (max + 1) % 3 ] = &mid;
1629 	return ApproximateTriangle_r( lm, dv2 );
1630 }
1631 
1632 
1633 
1634 /*
1635 ApproximateLightmap()
1636 determines if a raw lightmap can be approximated sufficiently with vertex colors
1637 */
1638 
ApproximateLightmap(rawLightmap_t * lm)1639 static qboolean ApproximateLightmap( rawLightmap_t *lm )
1640 {
1641 	int					n, num, i, x, y, pw[ 5 ], r;
1642 	bspDrawSurface_t	*ds;
1643 	surfaceInfo_t		*info;
1644 	mesh_t				src, *subdivided, *mesh;
1645 	bspDrawVert_t		*verts, *dv[ 3 ];
1646 	qboolean			approximated;
1647 
1648 
1649 	/* approximating? */
1650 	if( approximateTolerance <= 0 )
1651 		return qfalse;
1652 
1653 	/* test for jmonroe */
1654 	#if 0
1655 		/* don't approx lightmaps with styled twins */
1656 		if( lm->numStyledTwins > 0 )
1657 			return qfalse;
1658 
1659 		/* don't approx lightmaps with styles */
1660 		for( i = 1; i < MAX_LIGHTMAPS; i++ )
1661 		{
1662 			if( lm->styles[ i ] != LS_NONE )
1663 				return qfalse;
1664 		}
1665 	#endif
1666 
1667 	/* assume reduced until shadow detail is found */
1668 	approximated = qtrue;
1669 
1670 	/* walk the list of surfaces on this raw lightmap */
1671 	for( n = 0; n < lm->numLightSurfaces; n++ )
1672 	{
1673 		/* get surface */
1674 		num = lightSurfaces[ lm->firstLightSurface + n ];
1675 		ds = &bspDrawSurfaces[ num ];
1676 		info = &surfaceInfos[ num ];
1677 
1678 		/* assume not-reduced initially */
1679 		info->approximated = qfalse;
1680 
1681 		/* bail if lightmap doesn't match up */
1682 		if( info->lm != lm )
1683 			continue;
1684 
1685 		/* bail if not vertex lit */
1686 		if( info->si->noVertexLight )
1687 			continue;
1688 
1689 		/* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1690 		if( (info->maxs[ 0 ] - info->mins[ 0 ]) <= (2.0f * info->sampleSize) &&
1691 			(info->maxs[ 1 ] - info->mins[ 1 ]) <= (2.0f * info->sampleSize) &&
1692 			(info->maxs[ 2 ] - info->mins[ 2 ]) <= (2.0f * info->sampleSize) )
1693 		{
1694 			info->approximated = qtrue;
1695 			numSurfsVertexForced++;
1696 			continue;
1697 		}
1698 
1699 		/* handle the triangles */
1700 		switch( ds->surfaceType )
1701 		{
1702 			case MST_PLANAR:
1703 				/* get verts */
1704 				verts = yDrawVerts + ds->firstVert;
1705 
1706 				/* map the triangles */
1707 				info->approximated = qtrue;
1708 				for( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1709 				{
1710 					dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1711 					dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1712 					dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1713 					info->approximated = ApproximateTriangle_r( lm, dv );
1714 				}
1715 				break;
1716 
1717 			case MST_PATCH:
1718 				/* make a mesh from the drawsurf */
1719 				src.width = ds->patchWidth;
1720 				src.height = ds->patchHeight;
1721 				src.verts = &yDrawVerts[ ds->firstVert ];
1722 				//%	subdivided = SubdivideMesh( src, 8, 512 );
1723 				subdivided = SubdivideMesh2( src, info->patchIterations );
1724 
1725 				/* fit it to the curve and remove colinear verts on rows/columns */
1726 				PutMeshOnCurve( *subdivided );
1727 				mesh = RemoveLinearMeshColumnsRows( subdivided );
1728 				FreeMesh( subdivided );
1729 
1730 				/* get verts */
1731 				verts = mesh->verts;
1732 
1733 				/* map the mesh quads */
1734 				info->approximated = qtrue;
1735 				for( y = 0; y < (mesh->height - 1) && info->approximated; y++ )
1736 				{
1737 					for( x = 0; x < (mesh->width - 1) && info->approximated; x++ )
1738 					{
1739 						/* set indexes */
1740 						pw[ 0 ] = x + (y * mesh->width);
1741 						pw[ 1 ] = x + ((y + 1) * mesh->width);
1742 						pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1743 						pw[ 3 ] = x + 1 + (y * mesh->width);
1744 						pw[ 4 ] = x + (y * mesh->width);	/* same as pw[ 0 ] */
1745 
1746 						/* set radix */
1747 						r = (x + y) & 1;
1748 
1749 						/* get drawverts and map first triangle */
1750 						dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1751 						dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1752 						dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1753 						info->approximated = ApproximateTriangle_r( lm, dv );
1754 
1755 						/* get drawverts and map second triangle */
1756 						dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1757 						dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1758 						dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1759 						if( info->approximated )
1760 							info->approximated = ApproximateTriangle_r( lm, dv );
1761 					}
1762 				}
1763 
1764 				/* free the mesh */
1765 				FreeMesh( mesh );
1766 				break;
1767 
1768 			default:
1769 				break;
1770 		}
1771 
1772 		/* reduced? */
1773 		if( info->approximated == qfalse )
1774 			approximated = qfalse;
1775 		else
1776 			numSurfsVertexApproximated++;
1777 	}
1778 
1779 	/* return */
1780 	return approximated;
1781 }
1782 
1783 
1784 
1785 /*
1786 TestOutLightmapStamp()
1787 tests a stamp on a given lightmap for validity
1788 */
1789 
TestOutLightmapStamp(rawLightmap_t * lm,int lightmapNum,outLightmap_t * olm,int x,int y)1790 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y )
1791 {
1792 	int			sx, sy, ox, oy, offset;
1793 	float		*luxel;
1794 
1795 
1796 	/* bounds check */
1797 	if( x < 0 || y < 0 || (x + lm->w) > olm->customWidth || (y + lm->h) > olm->customHeight )
1798 		return qfalse;
1799 
1800 	/* solid lightmaps test a 1x1 stamp */
1801 	if( lm->solid[ lightmapNum ] )
1802 	{
1803 		offset = (y * olm->customWidth) + x;
1804 		if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1805 			return qfalse;
1806 		return qtrue;
1807 	}
1808 
1809 	/* test the stamp */
1810 	for( sy = 0; sy < lm->h; sy++ )
1811 	{
1812 		for( sx = 0; sx < lm->w; sx++ )
1813 		{
1814 			/* get luxel */
1815 			luxel = BSP_LUXEL( lightmapNum, sx, sy );
1816 			if( luxel[ 0 ] < 0.0f )
1817 				continue;
1818 
1819 			/* get bsp lightmap coords and test */
1820 			ox = x + sx;
1821 			oy = y + sy;
1822 			offset = (oy * olm->customWidth) + ox;
1823 			if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1824 				return qfalse;
1825 		}
1826 	}
1827 
1828 	/* stamp is empty */
1829 	return qtrue;
1830 }
1831 
1832 
1833 
1834 /*
1835 SetupOutLightmap()
1836 sets up an output lightmap
1837 */
1838 
SetupOutLightmap(rawLightmap_t * lm,outLightmap_t * olm)1839 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm )
1840 {
1841 	/* dummy check */
1842 	if( lm == NULL || olm == NULL )
1843 		return;
1844 
1845 	/* is this a "normal" bsp-stored lightmap? */
1846 	if( (lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize) || externalLightmaps )
1847 	{
1848 		olm->lightmapNum = numBSPLightmaps;
1849 		numBSPLightmaps++;
1850 
1851 		/* lightmaps are interleaved with light direction maps */
1852 		if( deluxemap )
1853 			numBSPLightmaps++;
1854 	}
1855 	else
1856 		olm->lightmapNum = -3;
1857 
1858 	/* set external lightmap number */
1859 	olm->extLightmapNum = -1;
1860 
1861 	/* set it up */
1862 	olm->numLightmaps = 0;
1863 	olm->customWidth = lm->customWidth;
1864 	olm->customHeight = lm->customHeight;
1865 	olm->freeLuxels = olm->customWidth * olm->customHeight;
1866 	olm->numShaders = 0;
1867 
1868 	/* allocate buffers */
1869 	olm->lightBits = safe_malloc( (olm->customWidth * olm->customHeight / 8) + 8 );
1870 	memset( olm->lightBits, 0, (olm->customWidth * olm->customHeight / 8) + 8 );
1871 	olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1872 	memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1873 	if( deluxemap )
1874 	{
1875 		olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1876 		memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1877 	}
1878 }
1879 
1880 
1881 
1882 /*
1883 FindOutLightmaps()
1884 for a given surface lightmap, find output lightmap pages and positions for it
1885 */
1886 
FindOutLightmaps(rawLightmap_t * lm)1887 static void FindOutLightmaps( rawLightmap_t *lm )
1888 {
1889 	int					i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
1890 	outLightmap_t		*olm;
1891 	surfaceInfo_t		*info;
1892 	float				*luxel, *deluxel;
1893 	vec3_t				color, direction;
1894 	byte				*pixel;
1895 	qboolean			ok;
1896 
1897 
1898 	/* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1899 	for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1900 		lm->outLightmapNums[ lightmapNum ] = -3;
1901 
1902 	/* can this lightmap be approximated with vertex color? */
1903 	if( ApproximateLightmap( lm ) )
1904 		return;
1905 
1906 	/* walk list */
1907 	for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1908 	{
1909 		/* early out */
1910 		if( lm->styles[ lightmapNum ] == LS_NONE )
1911 			continue;
1912 
1913 		/* don't store twinned lightmaps */
1914 		if( lm->twins[ lightmapNum ] != NULL )
1915 			continue;
1916 
1917 		/* if this is a styled lightmap, try some normalized locations first */
1918 		ok = qfalse;
1919 		if( lightmapNum > 0 && outLightmaps != NULL )
1920 		{
1921 			/* loop twice */
1922 			for( j = 0; j < 2; j++ )
1923 			{
1924 				/* try identical position */
1925 				for( i = 0; i < numOutLightmaps; i++ )
1926 				{
1927 					/* get the output lightmap */
1928 					olm = &outLightmaps[ i ];
1929 
1930 					/* simple early out test */
1931 					if( olm->freeLuxels < lm->used )
1932 						continue;
1933 
1934 					/* don't store non-custom raw lightmaps on custom bsp lightmaps */
1935 					if( olm->customWidth != lm->customWidth ||
1936 						olm->customHeight != lm->customHeight )
1937 						continue;
1938 
1939 					/* try identical */
1940 					if( j == 0 )
1941 					{
1942 						x = lm->lightmapX[ 0 ];
1943 						y = lm->lightmapY[ 0 ];
1944 						ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1945 					}
1946 
1947 					/* try shifting */
1948 					else
1949 					{
1950 						for( sy = -1; sy <= 1; sy++ )
1951 						{
1952 							for( sx = -1; sx <= 1; sx++ )
1953 							{
1954 								x = lm->lightmapX[ 0 ] + sx * (olm->customWidth >> 1);	//%	lm->w;
1955 								y = lm->lightmapY[ 0 ] + sy * (olm->customHeight >> 1);	//%	lm->h;
1956 								ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1957 
1958 								if( ok )
1959 									break;
1960 							}
1961 
1962 							if( ok )
1963 								break;
1964 						}
1965 					}
1966 
1967 					if( ok )
1968 						break;
1969 				}
1970 
1971 				if( ok )
1972 					break;
1973 			}
1974 		}
1975 
1976 		/* try normal placement algorithm */
1977 		if( ok == qfalse )
1978 		{
1979 			/* reset origin */
1980 			x = 0;
1981 			y = 0;
1982 
1983 			/* walk the list of lightmap pages */
1984 			for( i = 0; i < numOutLightmaps; i++ )
1985 			{
1986 				/* get the output lightmap */
1987 				olm = &outLightmaps[ i ];
1988 
1989 				/* simple early out test */
1990 				if( olm->freeLuxels < lm->used )
1991 					continue;
1992 
1993 				/* don't store non-custom raw lightmaps on custom bsp lightmaps */
1994 				if( olm->customWidth != lm->customWidth ||
1995 					olm->customHeight != lm->customHeight )
1996 					continue;
1997 
1998 				/* set maxs */
1999 				if( lm->solid[ lightmapNum ] )
2000 				{
2001 					xMax = olm->customWidth;
2002 					yMax = olm->customHeight;
2003 				}
2004 				else
2005 				{
2006 					xMax = (olm->customWidth - lm->w) + 1;
2007 					yMax = (olm->customHeight - lm->h) + 1;
2008 				}
2009 
2010 				/* walk the origin around the lightmap */
2011 				for( y = 0; y < yMax; y++ )
2012 				{
2013 					for( x = 0; x < xMax; x++ )
2014 					{
2015 						/* find a fine tract of lauhnd */
2016 						ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2017 
2018 						if( ok )
2019 							break;
2020 					}
2021 
2022 					if( ok )
2023 						break;
2024 				}
2025 
2026 				if( ok )
2027 					break;
2028 
2029 				/* reset x and y */
2030 				x = 0;
2031 				y = 0;
2032 			}
2033 		}
2034 
2035 		/* no match? */
2036 		if( ok == qfalse )
2037 		{
2038 			/* allocate two new output lightmaps */
2039 			numOutLightmaps += 2;
2040 			olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2041 			if( outLightmaps != NULL && numOutLightmaps > 2 )
2042 			{
2043 				memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );
2044 				free( outLightmaps );
2045 			}
2046 			outLightmaps = olm;
2047 
2048 			/* initialize both out lightmaps */
2049 			SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
2050 			SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
2051 
2052 			/* set out lightmap */
2053 			i = numOutLightmaps - 2;
2054 			olm = &outLightmaps[ i ];
2055 
2056 			/* set stamp xy origin to the first surface lightmap */
2057 			if( lightmapNum > 0 )
2058 			{
2059 				x = lm->lightmapX[ 0 ];
2060 				y = lm->lightmapY[ 0 ];
2061 			}
2062 		}
2063 
2064 		/* if this is a style-using lightmap, it must be exported */
2065 		if( lightmapNum > 0 && game->load != LoadRBSPFile )
2066 			olm->extLightmapNum = 0;
2067 
2068 		/* add the surface lightmap to the bsp lightmap */
2069 		lm->outLightmapNums[ lightmapNum ] = i;
2070 		lm->lightmapX[ lightmapNum ] = x;
2071 		lm->lightmapY[ lightmapNum ] = y;
2072 		olm->numLightmaps++;
2073 
2074 		/* add shaders */
2075 		for( i = 0; i < lm->numLightSurfaces; i++ )
2076 		{
2077 			/* get surface info */
2078 			info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2079 
2080 			/* test for shader */
2081 			for( j = 0; j < olm->numShaders; j++ )
2082 			{
2083 				if( olm->shaders[ j ] == info->si )
2084 					break;
2085 			}
2086 
2087 			/* if it doesn't exist, add it */
2088 			if( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS )
2089 			{
2090 				olm->shaders[ olm->numShaders ] = info->si;
2091 				olm->numShaders++;
2092 				numLightmapShaders++;
2093 			}
2094 		}
2095 
2096 		/* set maxs */
2097 		if( lm->solid[ lightmapNum ] )
2098 		{
2099 			xMax = 1;
2100 			yMax = 1;
2101 		}
2102 		else
2103 		{
2104 			xMax = lm->w;
2105 			yMax = lm->h;
2106 		}
2107 
2108 		/* mark the bits used */
2109 		for( y = 0; y < yMax; y++ )
2110 		{
2111 			for( x = 0; x < xMax; x++ )
2112 			{
2113 				/* get luxel */
2114 				luxel = BSP_LUXEL( lightmapNum, x, y );
2115 				deluxel = BSP_DELUXEL( x, y );
2116 				if( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ])
2117 					continue;
2118 
2119 				/* set minimum light */
2120 				if( lm->solid[ lightmapNum ] )
2121 				{
2122 					if( debug )
2123 						VectorSet( color, 255.0f, 0.0f, 0.0f );
2124 					else
2125 						VectorCopy( lm->solidColor[ lightmapNum ], color );
2126 				}
2127 				else
2128 					VectorCopy( luxel, color );
2129 
2130 				/* styles are not affected by minlight */
2131 				if( lightmapNum == 0 )
2132 				{
2133 					for( i = 0; i < 3; i++ )
2134 					{
2135 						if( color[ i ] < minLight[ i ] )
2136 							color[ i ] = minLight[ i ];
2137 					}
2138 				}
2139 
2140 				/* get bsp lightmap coords  */
2141 				ox = x + lm->lightmapX[ lightmapNum ];
2142 				oy = y + lm->lightmapY[ lightmapNum ];
2143 				offset = (oy * olm->customWidth) + ox;
2144 
2145 				/* flag pixel as used */
2146 				olm->lightBits[ offset >> 3 ] |= (1 << (offset & 7));
2147 				olm->freeLuxels--;
2148 
2149 				/* store color */
2150 				pixel = olm->bspLightBytes + (((oy * olm->customWidth) + ox) * 3);
2151 				ColorToBytes( color, pixel, lm->brightness );
2152 
2153 				/* store direction */
2154 				if( deluxemap )
2155 				{
2156 					/* normalize average light direction */
2157 					if( VectorNormalize( deluxel, direction ) )
2158 					{
2159 						/* encode [-1,1] in [0,255] */
2160 						pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
2161 						for( i = 0; i < 3; i++ )
2162 						{
2163 							temp = (direction[ i ] + 1.0f) * 127.5f;
2164 							if( temp < 0 )
2165 								pixel[ i ] = 0;
2166 							else if( temp > 255 )
2167 								pixel[ i ] = 255;
2168 							else
2169 								pixel[ i ] = temp;
2170 						}
2171 					}
2172 				}
2173 			}
2174 		}
2175 	}
2176 }
2177 
2178 
2179 
2180 /*
2181 CompareRawLightmap()
2182 compare function for qsort()
2183 */
2184 
CompareRawLightmap(const void * a,const void * b)2185 static int CompareRawLightmap( const void *a, const void *b )
2186 {
2187 	rawLightmap_t	*alm, *blm;
2188 	surfaceInfo_t	*aInfo, *bInfo;
2189 	int				i, min, diff;
2190 
2191 
2192 	/* get lightmaps */
2193 	alm = &rawLightmaps[ *((int*) a) ];
2194 	blm = &rawLightmaps[ *((int*) b) ];
2195 
2196 	/* get min number of surfaces */
2197 	min = (alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces);
2198 
2199 	/* iterate */
2200 	for( i = 0; i < min; i++ )
2201 	{
2202 		/* get surface info */
2203 		aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2204 		bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2205 
2206 		/* compare shader names */
2207 		diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2208 		if( diff != 0 )
2209 			return diff;
2210 	}
2211 
2212 	/* test style count */
2213 	diff = 0;
2214 	for( i = 0; i < MAX_LIGHTMAPS; i++ )
2215 		diff += blm->styles[ i ] - alm->styles[ i ];
2216 	if( diff )
2217 		return diff;
2218 
2219 	/* compare size */
2220 	diff = (blm->w * blm->h) - (alm->w * alm->h);
2221 	if( diff != 0 )
2222 		return diff;
2223 
2224 	/* must be equivalent */
2225 	return 0;
2226 }
2227 
2228 
2229 
2230 /*
2231 StoreSurfaceLightmaps()
2232 stores the surface lightmaps into the bsp as byte rgb triplets
2233 */
2234 
StoreSurfaceLightmaps(void)2235 void StoreSurfaceLightmaps( void )
2236 {
2237 	int					i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2238 	int					style, size, lightmapNum, lightmapNum2;
2239 	float				*normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2240 	vec3_t				sample, occludedSample, dirSample, colorMins, colorMaxs;
2241 	float				*deluxel, *bspDeluxel, *bspDeluxel2;
2242 	byte				*lb;
2243 	int					numUsed, numTwins, numTwinLuxels, numStored;
2244 	float				lmx, lmy, efficiency;
2245 	vec3_t				color;
2246 	bspDrawSurface_t	*ds, *parent, dsTemp;
2247 	surfaceInfo_t		*info;
2248 	rawLightmap_t		*lm, *lm2;
2249 	outLightmap_t		*olm;
2250 	bspDrawVert_t		*dv, *ydv, *dvParent;
2251 	char				dirname[ 1024 ], filename[ 1024 ];
2252 	shaderInfo_t		*csi;
2253 	char				lightmapName[ 128 ];
2254 	char				*rgbGenValues[ 256 ];
2255 	char				*alphaGenValues[ 256 ];
2256 
2257 
2258 	/* note it */
2259 	Sys_Printf( "--- StoreSurfaceLightmaps ---\n");
2260 
2261 	/* setup */
2262 	strcpy( dirname, source );
2263 	StripExtension( dirname );
2264 	memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2265 	memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2266 
2267 	/* -----------------------------------------------------------------
2268 	   average the sampled luxels into the bsp luxels
2269 	   ----------------------------------------------------------------- */
2270 
2271 	/* note it */
2272 	Sys_FPrintf( SYS_VRB, "Subsampling..." );
2273 
2274 	/* walk the list of raw lightmaps */
2275 	numUsed = 0;
2276 	numTwins = 0;
2277 	numTwinLuxels = 0;
2278 	numSolidLightmaps = 0;
2279 	for( i = 0; i < numRawLightmaps; i++ )
2280 	{
2281 		/* get lightmap */
2282 		lm = &rawLightmaps[ i ];
2283 
2284 		/* walk individual lightmaps */
2285 		for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2286 		{
2287 			/* early outs */
2288 			if( lm->superLuxels[ lightmapNum ] == NULL )
2289 				continue;
2290 
2291 			/* allocate bsp luxel storage */
2292 			if( lm->bspLuxels[ lightmapNum ] == NULL )
2293 			{
2294 				size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2295 				lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2296 				memset( lm->bspLuxels[ lightmapNum ], 0, size );
2297 			}
2298 
2299 			/* allocate radiosity lightmap storage */
2300 			if( bounce )
2301 			{
2302 				size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2303 				if( lm->radLuxels[ lightmapNum ] == NULL )
2304 					lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2305 				memset( lm->radLuxels[ lightmapNum ], 0, size );
2306 			}
2307 
2308 			/* average supersampled luxels */
2309 			for( y = 0; y < lm->h; y++ )
2310 			{
2311 				for( x = 0; x < lm->w; x++ )
2312 				{
2313 					/* subsample */
2314 					samples = 0.0f;
2315 					occludedSamples = 0.0f;
2316 					mappedSamples = 0;
2317 					VectorClear( sample );
2318 					VectorClear( occludedSample );
2319 					VectorClear( dirSample );
2320 					for( ly = 0; ly < superSample; ly++ )
2321 					{
2322 						for( lx = 0; lx < superSample; lx++ )
2323 						{
2324 							/* sample luxel */
2325 							sx = x * superSample + lx;
2326 							sy = y * superSample + ly;
2327 							luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2328 							deluxel = SUPER_DELUXEL( sx, sy );
2329 							normal = SUPER_NORMAL( sx, sy );
2330 							cluster = SUPER_CLUSTER( sx, sy );
2331 
2332 							/* sample deluxemap */
2333 							if( deluxemap && lightmapNum == 0 )
2334 								VectorAdd( dirSample, deluxel, dirSample );
2335 
2336 							/* keep track of used/occluded samples */
2337 							if( *cluster != CLUSTER_UNMAPPED )
2338 								mappedSamples++;
2339 
2340 							/* handle lightmap border? */
2341 							if( lightmapBorder && (sx == 0 || sx == (lm->sw - 1) || sy == 0 || sy == (lm->sh - 1) ) && luxel[ 3 ] > 0.0f )
2342 							{
2343 								VectorSet( sample, 255.0f, 0.0f, 0.0f );
2344 								samples += 1.0f;
2345 							}
2346 
2347 							/* handle debug */
2348 							else if( debug && *cluster < 0 )
2349 							{
2350 								if( *cluster == CLUSTER_UNMAPPED )
2351 									VectorSet( luxel, 255, 204, 0 );
2352 								else if( *cluster == CLUSTER_OCCLUDED )
2353 									VectorSet( luxel, 255, 0, 255 );
2354 								else if( *cluster == CLUSTER_FLOODED )
2355 									VectorSet( luxel, 0, 32, 255 );
2356 								VectorAdd( occludedSample, luxel, occludedSample );
2357 								occludedSamples += 1.0f;
2358 							}
2359 
2360 							/* normal luxel handling */
2361 							else if( luxel[ 3 ] > 0.0f )
2362 							{
2363 								/* handle lit or flooded luxels */
2364 								if( *cluster > 0 || *cluster == CLUSTER_FLOODED )
2365 								{
2366 									VectorAdd( sample, luxel, sample );
2367 									samples += luxel[ 3 ];
2368 								}
2369 
2370 								/* handle occluded or unmapped luxels */
2371 								else
2372 								{
2373 									VectorAdd( occludedSample, luxel, occludedSample );
2374 									occludedSamples += luxel[ 3 ];
2375 								}
2376 
2377 								/* handle style debugging */
2378 								if( debug && lightmapNum > 0 && x < 2 && y < 2 )
2379 								{
2380 									VectorCopy( debugColors[ 0 ], sample );
2381 									samples = 1;
2382 								}
2383 							}
2384 						}
2385 					}
2386 
2387 					/* only use occluded samples if necessary */
2388 					if( samples <= 0.0f )
2389 					{
2390 						VectorCopy( occludedSample, sample );
2391 						samples = occludedSamples;
2392 					}
2393 
2394 					/* get luxels */
2395 					luxel = SUPER_LUXEL( lightmapNum, x, y );
2396 					deluxel = SUPER_DELUXEL( x, y );
2397 
2398 					/* store light direction */
2399 					if( deluxemap && lightmapNum == 0 )
2400 						VectorCopy( dirSample, deluxel );
2401 
2402 					/* store the sample back in super luxels */
2403 					if( samples > 0.01f )
2404 					{
2405 						VectorScale( sample, (1.0f / samples), luxel );
2406 						luxel[ 3 ] = 1.0f;
2407 					}
2408 
2409 					/* if any samples were mapped in any way, store ambient color */
2410 					else if( mappedSamples > 0 )
2411 					{
2412 						if( lightmapNum == 0 )
2413 							VectorCopy( ambientColor, luxel );
2414 						else
2415 							VectorClear( luxel );
2416 						luxel[ 3 ] = 1.0f;
2417 					}
2418 
2419 					/* store a bogus value to be fixed later */
2420 					else
2421 					{
2422 						VectorClear( luxel );
2423 						luxel[ 3 ] = -1.0f;
2424 					}
2425 				}
2426 			}
2427 
2428 			/* setup */
2429 			lm->used = 0;
2430 			ClearBounds( colorMins, colorMaxs );
2431 
2432 			/* clean up and store into bsp luxels */
2433 			for( y = 0; y < lm->h; y++ )
2434 			{
2435 				for( x = 0; x < lm->w; x++ )
2436 				{
2437 					/* get luxels */
2438 					luxel = SUPER_LUXEL( lightmapNum, x, y );
2439 					deluxel = SUPER_DELUXEL( x, y );
2440 
2441 					/* copy light direction */
2442 					if( deluxemap && lightmapNum == 0 )
2443 						VectorCopy( deluxel, dirSample );
2444 
2445 					/* is this a valid sample? */
2446 					if( luxel[ 3 ] > 0.0f )
2447 					{
2448 						VectorCopy( luxel, sample );
2449 						samples = luxel[ 3 ];
2450 						numUsed++;
2451 						lm->used++;
2452 
2453 						/* fix negative samples */
2454 						for( j = 0; j < 3; j++ )
2455 						{
2456 							if( sample[ j ] < 0.0f )
2457 								sample[ j ] = 0.0f;
2458 						}
2459 					}
2460 					else
2461 					{
2462 						/* nick an average value from the neighbors */
2463 						VectorClear( sample );
2464 						VectorClear( dirSample );
2465 						samples = 0.0f;
2466 
2467 						/* fixme: why is this disabled?? */
2468 						for( sy = (y - 1); sy <= (y + 1); sy++ )
2469 						{
2470 							if( sy < 0 || sy >= lm->h )
2471 								continue;
2472 
2473 							for( sx = (x - 1); sx <= (x + 1); sx++ )
2474 							{
2475 								if( sx < 0 || sx >= lm->w || (sx == x && sy == y) )
2476 									continue;
2477 
2478 								/* get neighbor's particulars */
2479 								luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2480 								if( luxel[ 3 ] < 0.0f )
2481 									continue;
2482 								VectorAdd( sample, luxel, sample );
2483 								samples += luxel[ 3 ];
2484 							}
2485 						}
2486 
2487 						/* no samples? */
2488 						if( samples == 0.0f )
2489 						{
2490 							VectorSet( sample, -1.0f, -1.0f, -1.0f );
2491 							samples = 1.0f;
2492 						}
2493 						else
2494 						{
2495 							numUsed++;
2496 							lm->used++;
2497 
2498 							/* fix negative samples */
2499 							for( j = 0; j < 3; j++ )
2500 							{
2501 								if( sample[ j ] < 0.0f )
2502 									sample[ j ] = 0.0f;
2503 							}
2504 						}
2505 					}
2506 
2507 					/* scale the sample */
2508 					VectorScale( sample, (1.0f / samples), sample );
2509 
2510 					/* store the sample in the radiosity luxels */
2511 					if( bounce > 0 )
2512 					{
2513 						radLuxel = RAD_LUXEL( lightmapNum, x, y );
2514 						VectorCopy( sample, radLuxel );
2515 
2516 						/* if only storing bounced light, early out here */
2517 						if( bounceOnly && !bouncing )
2518 							continue;
2519 					}
2520 
2521 					/* store the sample in the bsp luxels */
2522 					bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2523 					bspDeluxel = BSP_DELUXEL( x, y );
2524 
2525 					VectorAdd( bspLuxel, sample, bspLuxel );
2526 					if( deluxemap && lightmapNum == 0 )
2527 						VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2528 
2529 					/* add color to bounds for solid checking */
2530 					if( samples > 0.0f )
2531 						AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2532 				}
2533 			}
2534 
2535 			/* set solid color */
2536 			lm->solid[ lightmapNum ] = qfalse;
2537 			VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2538 			VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2539 
2540 			/* nocollapse prevents solid lightmaps */
2541 			if( noCollapse == qfalse )
2542 			{
2543 				/* check solid color */
2544 				VectorSubtract( colorMaxs, colorMins, sample );
2545 				if( (sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON) ||
2546 					(lm->w <= 2 && lm->h <= 2) )	/* small lightmaps get forced to solid color */
2547 				{
2548 					/* set to solid */
2549 					VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2550 					lm->solid[ lightmapNum ] = qtrue;
2551 					numSolidLightmaps++;
2552 				}
2553 
2554 				/* if all lightmaps aren't solid, then none of them are solid */
2555 				if( lm->solid[ lightmapNum ] != lm->solid[ 0 ] )
2556 				{
2557 					for( y = 0; y < MAX_LIGHTMAPS; y++ )
2558 					{
2559 						if( lm->solid[ y ] )
2560 							numSolidLightmaps--;
2561 						lm->solid[ y ] = qfalse;
2562 					}
2563 				}
2564 			}
2565 
2566 			/* wrap bsp luxels if necessary */
2567 			if( lm->wrap[ 0 ] )
2568 			{
2569 				for( y = 0; y < lm->h; y++ )
2570 				{
2571 					bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2572 					bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2573 					VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2574 					VectorScale( bspLuxel, 0.5f, bspLuxel );
2575 					VectorCopy( bspLuxel, bspLuxel2 );
2576 					if( deluxemap && lightmapNum == 0 )
2577 					{
2578 						bspDeluxel = BSP_DELUXEL( 0, y );
2579 						bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2580 						VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2581 						VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2582 						VectorCopy( bspDeluxel, bspDeluxel2 );
2583 					}
2584 				}
2585 			}
2586 			if( lm->wrap[ 1 ] )
2587 			{
2588 				for( x = 0; x < lm->w; x++ )
2589 				{
2590 					bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2591 					bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2592 					VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2593 					VectorScale( bspLuxel, 0.5f, bspLuxel );
2594 					VectorCopy( bspLuxel, bspLuxel2 );
2595 					if( deluxemap && lightmapNum == 0 )
2596 					{
2597 						bspDeluxel = BSP_DELUXEL( x, 0 );
2598 						bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2599 						VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2600 						VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2601 						VectorCopy( bspDeluxel, bspDeluxel2 );
2602 					}
2603 				}
2604 			}
2605 		}
2606 	}
2607 
2608 	/* -----------------------------------------------------------------
2609 	   collapse non-unique lightmaps
2610 	   ----------------------------------------------------------------- */
2611 
2612 	if( noCollapse == qfalse && deluxemap == qfalse )
2613 	{
2614 		/* note it */
2615 		Sys_FPrintf( SYS_VRB, "collapsing..." );
2616 
2617 		/* set all twin refs to null */
2618 		for( i = 0; i < numRawLightmaps; i++ )
2619 		{
2620 			for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2621 			{
2622 				rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2623 				rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2624 				rawLightmaps[ i ].numStyledTwins = 0;
2625 			}
2626 		}
2627 
2628 		/* walk the list of raw lightmaps */
2629 		for( i = 0; i < numRawLightmaps; i++ )
2630 		{
2631 			/* get lightmap */
2632 			lm = &rawLightmaps[ i ];
2633 
2634 			/* walk lightmaps */
2635 			for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2636 			{
2637 				/* early outs */
2638 				if( lm->bspLuxels[ lightmapNum ] == NULL ||
2639 					lm->twins[ lightmapNum ] != NULL )
2640 					continue;
2641 
2642 				/* find all lightmaps that are virtually identical to this one */
2643 				for( j = i + 1; j < numRawLightmaps; j++ )
2644 				{
2645 					/* get lightmap */
2646 					lm2 = &rawLightmaps[ j ];
2647 
2648 					/* walk lightmaps */
2649 					for( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
2650 					{
2651 						/* early outs */
2652 						if( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
2653 							lm2->twins[ lightmapNum2 ] != NULL )
2654 							continue;
2655 
2656 						/* compare them */
2657 						if( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2658 						{
2659 							/* merge and set twin */
2660 							if( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2661 							{
2662 								lm2->twins[ lightmapNum2 ] = lm;
2663 								lm2->twinNums[ lightmapNum2 ] = lightmapNum;
2664 								numTwins++;
2665 								numTwinLuxels += (lm->w * lm->h);
2666 
2667 								/* count styled twins */
2668 								if( lightmapNum > 0 )
2669 									lm->numStyledTwins++;
2670 							}
2671 						}
2672 					}
2673 				}
2674 			}
2675 		}
2676 	}
2677 
2678 	/* -----------------------------------------------------------------
2679 	   sort raw lightmaps by shader
2680 	   ----------------------------------------------------------------- */
2681 
2682 	/* note it */
2683 	Sys_FPrintf( SYS_VRB, "sorting..." );
2684 
2685 	/* allocate a new sorted list */
2686 	if( sortLightmaps == NULL )
2687 		sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
2688 
2689 	/* fill it out and sort it */
2690 	for( i = 0; i < numRawLightmaps; i++ )
2691 		sortLightmaps[ i ] = i;
2692 	qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
2693 
2694 	/* -----------------------------------------------------------------
2695 	   allocate output lightmaps
2696 	   ----------------------------------------------------------------- */
2697 
2698 	/* note it */
2699 	Sys_FPrintf( SYS_VRB, "allocating..." );
2700 
2701 	/* kill all existing output lightmaps */
2702 	if( outLightmaps != NULL )
2703 	{
2704 		for( i = 0; i < numOutLightmaps; i++ )
2705 		{
2706 			free( outLightmaps[ i ].lightBits );
2707 			free( outLightmaps[ i ].bspLightBytes );
2708 		}
2709 		free( outLightmaps );
2710 		outLightmaps = NULL;
2711 	}
2712 
2713 	numLightmapShaders = 0;
2714 	numOutLightmaps = 0;
2715 	numBSPLightmaps = 0;
2716 	numExtLightmaps = 0;
2717 
2718 	/* find output lightmap */
2719 	for( i = 0; i < numRawLightmaps; i++ )
2720 	{
2721 		lm = &rawLightmaps[ sortLightmaps[ i ] ];
2722 		FindOutLightmaps( lm );
2723 	}
2724 
2725 	/* set output numbers in twinned lightmaps */
2726 	for( i = 0; i < numRawLightmaps; i++ )
2727 	{
2728 		/* get lightmap */
2729 		lm = &rawLightmaps[ sortLightmaps[ i ] ];
2730 
2731 		/* walk lightmaps */
2732 		for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2733 		{
2734 			/* get twin */
2735 			lm2 = lm->twins[ lightmapNum ];
2736 			if( lm2 == NULL )
2737 				continue;
2738 			lightmapNum2 = lm->twinNums[ lightmapNum ];
2739 
2740 			/* find output lightmap from twin */
2741 			lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
2742 			lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
2743 			lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
2744 		}
2745 	}
2746 
2747 	/* -----------------------------------------------------------------
2748 	   store output lightmaps
2749 	   ----------------------------------------------------------------- */
2750 
2751 	/* note it */
2752 	Sys_FPrintf( SYS_VRB, "storing..." );
2753 
2754 	/* count the bsp lightmaps and allocate space */
2755 	if( bspLightBytes != NULL )
2756 		free( bspLightBytes );
2757 	if( numBSPLightmaps == 0 || externalLightmaps )
2758 	{
2759 		numBSPLightBytes = 0;
2760 		bspLightBytes = NULL;
2761 	}
2762 	else
2763 	{
2764 		numBSPLightBytes = (numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3);
2765 		bspLightBytes = safe_malloc( numBSPLightBytes );
2766 		memset( bspLightBytes, 0, numBSPLightBytes );
2767 	}
2768 
2769 	/* walk the list of output lightmaps */
2770 	for( i = 0; i < numOutLightmaps; i++ )
2771 	{
2772 		/* get output lightmap */
2773 		olm = &outLightmaps[ i ];
2774 
2775 		/* is this a valid bsp lightmap? */
2776 		if( olm->lightmapNum >= 0 && !externalLightmaps )
2777 		{
2778 			/* copy lighting data */
2779 			lb = bspLightBytes + (olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3);
2780 			memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
2781 
2782 			/* copy direction data */
2783 			if( deluxemap )
2784 			{
2785 				lb = bspLightBytes + ((olm->lightmapNum + 1) * game->lightmapSize * game->lightmapSize * 3);
2786 				memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
2787 			}
2788 		}
2789 
2790 		/* external lightmap? */
2791 		if( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps )
2792 		{
2793 			/* make a directory for the lightmaps */
2794 			Q_mkdir( dirname );
2795 
2796 			/* set external lightmap number */
2797 			olm->extLightmapNum = numExtLightmaps;
2798 
2799 			/* write lightmap */
2800 			sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2801 			Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2802 			WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
2803 			numExtLightmaps++;
2804 
2805 			/* write deluxemap */
2806 			if( deluxemap )
2807 			{
2808 				sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2809 				Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2810 				WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
2811 				numExtLightmaps++;
2812 
2813 				if( debugDeluxemap )
2814 					olm->extLightmapNum++;
2815 			}
2816 		}
2817 	}
2818 
2819 	if( numExtLightmaps > 0 )
2820 		Sys_FPrintf( SYS_VRB, "\n" );
2821 
2822 	/* delete unused external lightmaps */
2823 	for( i = numExtLightmaps; i; i++ )
2824 	{
2825 		/* determine if file exists */
2826 		sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
2827 		if( !FileExists( filename ) )
2828 			break;
2829 
2830 		/* delete it */
2831 		remove( filename );
2832 	}
2833 
2834 	/* -----------------------------------------------------------------
2835 	   project the lightmaps onto the bsp surfaces
2836 	   ----------------------------------------------------------------- */
2837 
2838 	/* note it */
2839 	Sys_FPrintf( SYS_VRB, "projecting..." );
2840 
2841 	/* walk the list of surfaces */
2842 	for( i = 0; i < numBSPDrawSurfaces; i++ )
2843 	{
2844 		/* get the surface and info */
2845 		ds = &bspDrawSurfaces[ i ];
2846 		info = &surfaceInfos[ i ];
2847 		lm = info->lm;
2848 		olm = NULL;
2849 
2850 		/* handle surfaces with identical parent */
2851 		if( info->parentSurfaceNum >= 0 )
2852 		{
2853 			/* preserve original data and get parent */
2854 			parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
2855 			memcpy( &dsTemp, ds, sizeof( *ds ) );
2856 
2857 			/* overwrite child with parent data */
2858 			memcpy( ds, parent, sizeof( *ds ) );
2859 
2860 			/* restore key parts */
2861 			ds->fogNum = dsTemp.fogNum;
2862 			ds->firstVert = dsTemp.firstVert;
2863 			ds->firstIndex = dsTemp.firstIndex;
2864 			memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
2865 
2866 			/* set vertex data */
2867 			dv = &bspDrawVerts[ ds->firstVert ];
2868 			dvParent = &bspDrawVerts[ parent->firstVert ];
2869 			for( j = 0; j < ds->numVerts; j++ )
2870 			{
2871 				memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
2872 				memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
2873 			}
2874 
2875 			/* skip the rest */
2876 			continue;
2877 		}
2878 
2879 		/* handle vertex lit or approximated surfaces */
2880 		else if( lm == NULL || lm->outLightmapNums[ 0 ] < 0 )
2881 		{
2882 			for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2883 			{
2884 				ds->lightmapNum[ lightmapNum ] = -3;
2885 				ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
2886 			}
2887 		}
2888 
2889 		/* handle lightmapped surfaces */
2890 		else
2891 		{
2892 			/* walk lightmaps */
2893 			for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2894 			{
2895 				/* set style */
2896 				ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2897 
2898 				/* handle unused style */
2899 				if( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
2900 				{
2901 					ds->lightmapNum[ lightmapNum ] = -3;
2902 					continue;
2903 				}
2904 
2905 				/* get output lightmap */
2906 				olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
2907 
2908 				/* set bsp lightmap number */
2909 				ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
2910 
2911 				/* deluxemap debugging makes the deluxemap visible */
2912 				if( deluxemap && debugDeluxemap && lightmapNum == 0 )
2913 					ds->lightmapNum[ lightmapNum ]++;
2914 
2915 				/* calc lightmap origin in texture space */
2916 				lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
2917 				lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
2918 
2919 				/* calc lightmap st coords */
2920 				dv = &bspDrawVerts[ ds->firstVert ];
2921 				ydv = &yDrawVerts[ ds->firstVert ];
2922 				for( j = 0; j < ds->numVerts; j++ )
2923 				{
2924 					if( lm->solid[ lightmapNum ] )
2925 					{
2926 						dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (0.5f / (float) olm->customWidth);
2927 						dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (0.5f / (float) olm->customWidth);
2928 					}
2929 					else
2930 					{
2931 						dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (ydv[ j ].lightmap[ 0 ][ 0 ] / (superSample * olm->customWidth));
2932 						dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (ydv[ j ].lightmap[ 0 ][ 1 ] / (superSample * olm->customHeight));
2933 					}
2934 				}
2935 			}
2936 		}
2937 
2938 		/* store vertex colors */
2939 		dv = &bspDrawVerts[ ds->firstVert ];
2940 		for( j = 0; j < ds->numVerts; j++ )
2941 		{
2942 			/* walk lightmaps */
2943 			for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2944 			{
2945 				/* handle unused style */
2946 				if( ds->vertexStyles[ lightmapNum ] == LS_NONE )
2947 					VectorClear( color );
2948 				else
2949 				{
2950 					/* get vertex color */
2951 					luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
2952 					VectorCopy( luxel, color );
2953 
2954 					/* set minimum light */
2955 					if( lightmapNum == 0 )
2956 					{
2957 						for( k = 0; k < 3; k++ )
2958 							if( color[ k ] < minVertexLight[ k ] )
2959 								color[ k ] = minVertexLight[ k ];
2960 					}
2961 				}
2962 
2963 				/* store to bytes */
2964 				if( !info->si->noVertexLight )
2965 					ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
2966 			}
2967 		}
2968 
2969 		/* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
2970 		if( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile )	//%	info->si->styleMarker > 0 )
2971 		{
2972 			qboolean	dfEqual;
2973 			char		key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
2974 
2975 
2976 			/* setup */
2977 			sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
2978 			dv = &bspDrawVerts[ ds->firstVert ];
2979 
2980 			/* depthFunc equal? */
2981 			if( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED )
2982 				dfEqual = qtrue;
2983 			else
2984 				dfEqual = qfalse;
2985 
2986 			/* generate stages for styled lightmaps */
2987 			for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2988 			{
2989 				/* early out */
2990 				style = lm->styles[ lightmapNum ];
2991 				if( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
2992 					continue;
2993 
2994 				/* get output lightmap */
2995 				olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
2996 
2997 				/* lightmap name */
2998 				if( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] )
2999 					strcpy( lightmapName, "$lightmap" );
3000 				else
3001 					sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3002 
3003 				/* get rgbgen string */
3004 				if( rgbGenValues[ style ] == NULL )
3005 				{
3006 					sprintf( key, "_style%drgbgen", style );
3007 					rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3008 					if( rgbGenValues[ style ][ 0 ] == '\0' )
3009 						rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3010 				}
3011 				rgbGen[ 0 ] = '\0';
3012 				if( rgbGenValues[ style ][ 0 ] != '\0' )
3013 					sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3014 				else
3015 					rgbGen[ 0 ] = '\0';
3016 
3017 				/* get alphagen string */
3018 				if( alphaGenValues[ style ] == NULL )
3019 				{
3020 					sprintf( key, "_style%dalphagen", style );
3021 					alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3022 				}
3023 				if( alphaGenValues[ style ][ 0 ] != '\0' )
3024 					sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3025 				else
3026 					alphaGen[ 0 ] = '\0';
3027 
3028 				/* calculate st offset */
3029 				lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3030 				lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3031 
3032 				/* create additional stage */
3033 				if( lmx == 0.0f && lmy == 0.0f )
3034 				{
3035 					sprintf( styleStage,	"\t{\n"
3036 											"\t\tmap %s\n"										/* lightmap */
3037 											"\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3038 											"%s"												/* depthFunc equal */
3039 											"%s"												/* rgbGen */
3040 											"%s"												/* alphaGen */
3041 											"\t\ttcGen lightmap\n"
3042 											"\t}\n",
3043 						lightmapName,
3044 						(dfEqual ? "\t\tdepthFunc equal\n" : ""),
3045 						rgbGen,
3046 						alphaGen );
3047 				}
3048 				else
3049 				{
3050 					sprintf( styleStage,	"\t{\n"
3051 											"\t\tmap %s\n"										/* lightmap */
3052 											"\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3053 											"%s"												/* depthFunc equal */
3054 											"%s"												/* rgbGen */
3055 											"%s"												/* alphaGen */
3056 											"\t\ttcGen lightmap\n"
3057 											"\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n"			/* st offset */
3058 											"\t}\n",
3059 						lightmapName,
3060 						(dfEqual ? "\t\tdepthFunc equal\n" : ""),
3061 						rgbGen,
3062 						alphaGen,
3063 						lmx, lmy );
3064 
3065 				}
3066 
3067 				/* concatenate */
3068 				strcat( styleStages, styleStage );
3069 			}
3070 
3071 			/* create custom shader */
3072 			if( info->si->styleMarker == 2 )
3073 				csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3074 			else
3075 				csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3076 
3077 			/* emit remap command */
3078 			//%	EmitVertexRemapShader( csi->shader, info->si->shader );
3079 
3080 			/* store it */
3081 			//%	Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3082 			ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3083 			//%	Sys_Printf( ")\n" );
3084 		}
3085 
3086 		/* devise a custom shader for this surface (fixme: make this work with light styles) */
3087 		else if( olm != NULL && lm != NULL && !externalLightmaps &&
3088 			(olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize) )
3089 		{
3090 			/* get output lightmap */
3091 			olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3092 
3093 			/* do some name mangling */
3094 			sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3095 
3096 			/* create custom shader */
3097 			csi = CustomShader( info->si, "$lightmap", lightmapName );
3098 
3099 			/* store it */
3100 			//%	Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3101 			ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3102 			//%	Sys_Printf( ")\n" );
3103 		}
3104 
3105 		/* use the normal plain-jane shader */
3106 		else
3107 			ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3108 	}
3109 
3110 	/* finish */
3111 	Sys_FPrintf( SYS_VRB, "done.\n" );
3112 
3113 	/* calc num stored */
3114 	numStored = numBSPLightBytes / 3;
3115 	efficiency = (numStored <= 0)
3116 		? 0
3117 		: (float) numUsed / (float) numStored;
3118 
3119 	/* print stats */
3120 	Sys_Printf( "%9d luxels used\n", numUsed );
3121 	Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3122 	Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3123 	Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3124 	Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3125 	Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3126 	Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3127 	Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3128 	Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3129 
3130 	/* write map shader file */
3131 	WriteMapShaderFile();
3132 }
3133 
3134 
3135 
3136 
3137