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