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