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