]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/lightmaps_ydnar.c
Merge commit 'dfce2da577f1e56886ad26e58e37d9eda2d7c8a3' into garux-merge
[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 #include <glib.h>
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_FPrintf( SYS_WRN, "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_FPrintf( SYS_WRN, "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_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
241                         continue;
242                 }
243                 if ( width != game->lightmapSize || height != game->lightmapSize ) {
244                         Sys_FPrintf( SYS_WRN, "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_FPrintf( SYS_WRN, "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         /* then lightmap sample size */
911         if ( aInfo->sampleSize < bInfo->sampleSize ) {
912                 return 1;
913         }
914         else if ( aInfo->sampleSize > bInfo->sampleSize ) {
915                 return -1;
916         }
917
918         /* then lightmap axis */
919         for ( i = 0; i < 3; i++ )
920         {
921                 if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
922                         return 1;
923                 }
924                 else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
925                         return -1;
926                 }
927         }
928
929         /* then plane */
930         if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
931                 return 1;
932         }
933         else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
934                 return -1;
935         }
936         else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
937                 for ( i = 0; i < 4; i++ )
938                 {
939                         if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
940                                 return 1;
941                         }
942                         else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
943                                 return -1;
944                         }
945                 }
946         }
947
948         /* then position in world */
949         for ( i = 0; i < 3; i++ )
950         {
951                 if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
952                         return 1;
953                 }
954                 else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
955                         return -1;
956                 }
957         }
958
959         /* these are functionally identical (this should almost never happen) */
960         return 0;
961 }
962
963
964
965 /*
966    SetupSurfaceLightmaps()
967    allocates lightmaps for every surface in the bsp that needs one
968    this depends on yDrawVerts being allocated
969  */
970
971 void SetupSurfaceLightmaps( void ){
972         int i, j, k, s,num, num2;
973         bspModel_t          *model;
974         bspLeaf_t           *leaf;
975         bspDrawSurface_t    *ds;
976         surfaceInfo_t       *info, *info2;
977         rawLightmap_t       *lm;
978         qboolean added;
979         vec3_t mapSize, entityOrigin;
980
981
982         /* note it */
983         Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
984
985         /* determine supersample amount */
986         if ( superSample < 1 ) {
987                 superSample = 1;
988         }
989         else if ( superSample > 8 ) {
990                 Sys_FPrintf( SYS_WRN, "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
991                 superSample = 8;
992         }
993
994         /* clear map bounds */
995         ClearBounds( mapMins, mapMaxs );
996
997         /* allocate a list of surface clusters */
998         numSurfaceClusters = 0;
999         maxSurfaceClusters = numBSPLeafSurfaces;
1000         surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
1001         memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
1002
1003         /* allocate a list for per-surface info */
1004         surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
1005         memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
1006         for ( i = 0; i < numBSPDrawSurfaces; i++ )
1007                 surfaceInfos[ i ].childSurfaceNum = -1;
1008
1009         /* allocate a list of surface indexes to be sorted */
1010         sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
1011         memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
1012
1013         /* walk each model in the bsp */
1014         for ( i = 0; i < numBSPModels; i++ )
1015         {
1016                 /* get model */
1017                 model = &bspModels[ i ];
1018
1019                 /* walk the list of surfaces in this model and fill out the info structs */
1020                 for ( j = 0; j < model->numBSPSurfaces; j++ )
1021                 {
1022                         /* make surface index */
1023                         num = model->firstBSPSurface + j;
1024
1025                         /* copy index to sort list */
1026                         sortSurfaces[ num ] = num;
1027
1028                         /* get surface and info */
1029                         ds = &bspDrawSurfaces[ num ];
1030                         info = &surfaceInfos[ num ];
1031
1032                         /* set entity origin */
1033                         if ( ds->numVerts > 0 ) {
1034                                 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
1035                         }
1036                         else{
1037                                 VectorClear( entityOrigin );
1038                         }
1039
1040                         /* basic setup */
1041                         info->modelindex = i;
1042                         info->lm = NULL;
1043                         info->plane = NULL;
1044                         info->firstSurfaceCluster = numSurfaceClusters;
1045
1046                         /* get extra data */
1047                         info->si = GetSurfaceExtraShaderInfo( num );
1048                         if ( info->si == NULL ) {
1049                                 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
1050                         }
1051                         info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1052                         info->entityNum = GetSurfaceExtraEntityNum( num );
1053                         info->castShadows = GetSurfaceExtraCastShadows( num );
1054                         info->recvShadows = GetSurfaceExtraRecvShadows( num );
1055                         info->sampleSize = GetSurfaceExtraSampleSize( num );
1056                         info->longestCurve = GetSurfaceExtraLongestCurve( num );
1057                         info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1058                         GetSurfaceExtraLightmapAxis( num, info->axis );
1059
1060                         /* mark parent */
1061                         if ( info->parentSurfaceNum >= 0 ) {
1062                                 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1063                         }
1064
1065                         /* determine surface bounds */
1066                         ClearBounds( info->mins, info->maxs );
1067                         for ( k = 0; k < ds->numVerts; k++ )
1068                         {
1069                                 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1070                                 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1071                         }
1072
1073                         /* find all the bsp clusters the surface falls into */
1074                         for ( k = 0; k < numBSPLeafs; k++ )
1075                         {
1076                                 /* get leaf */
1077                                 leaf = &bspLeafs[ k ];
1078
1079                                 /* test bbox */
1080                                 if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1081                                          leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1082                                          leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
1083                                         continue;
1084                                 }
1085
1086                                 /* test leaf surfaces */
1087                                 for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1088                                 {
1089                                         if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
1090                                                 if ( numSurfaceClusters >= maxSurfaceClusters ) {
1091                                                         Error( "maxSurfaceClusters exceeded" );
1092                                                 }
1093                                                 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1094                                                 numSurfaceClusters++;
1095                                                 info->numSurfaceClusters++;
1096                                         }
1097                                 }
1098                         }
1099
1100                         /* determine if surface is planar */
1101                         if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
1102                                 /* make a plane */
1103                                 info->plane = safe_malloc( 4 * sizeof( float ) );
1104                                 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1105                                 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1106                         }
1107
1108                         /* determine if surface requires a lightmap */
1109                         if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
1110                                  ds->surfaceType == MST_FOLIAGE ||
1111                                 ( info->si->compileFlags & C_VERTEXLIT ) ||
1112                                 nolm == qtrue ) {
1113                                 numSurfsVertexLit++;
1114                         }
1115                         else
1116                         {
1117                                 numSurfsLightmapped++;
1118                                 info->hasLightmap = qtrue;
1119                         }
1120                 }
1121         }
1122
1123         /* find longest map distance */
1124         VectorSubtract( mapMaxs, mapMins, mapSize );
1125         maxMapDistance = VectorLength( mapSize );
1126
1127         /* sort the surfaces info list */
1128         qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1129
1130         /* allocate a list of surfaces that would go into raw lightmaps */
1131         numLightSurfaces = 0;
1132         lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1133         memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1134
1135         /* allocate a list of raw lightmaps */
1136         numRawSuperLuxels = 0;
1137         numRawLightmaps = 0;
1138         rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1139         memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1140
1141         /* walk the list of sorted surfaces */
1142         for ( i = 0; i < numBSPDrawSurfaces; i++ )
1143         {
1144                 /* get info and attempt early out */
1145                 num = sortSurfaces[ i ];
1146                 ds = &bspDrawSurfaces[ num ];
1147                 info = &surfaceInfos[ num ];
1148                 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1149                         continue;
1150                 }
1151
1152                 /* allocate a new raw lightmap */
1153                 lm = &rawLightmaps[ numRawLightmaps ];
1154                 numRawLightmaps++;
1155
1156                 /* set it up */
1157                 lm->splotchFix = info->si->splotchFix;
1158                 lm->firstLightSurface = numLightSurfaces;
1159                 lm->numLightSurfaces = 0;
1160                 /* vortex: multiply lightmap sample size by -samplescale */
1161                 if ( sampleScale > 0 ) {
1162                         lm->sampleSize = info->sampleSize * sampleScale;
1163                 }
1164                 else{
1165                         lm->sampleSize = info->sampleSize;
1166                 }
1167                 lm->actualSampleSize = lm->sampleSize;
1168                 lm->entityNum = info->entityNum;
1169                 lm->recvShadows = info->recvShadows;
1170                 lm->brightness = info->si->lmBrightness;
1171                 lm->filterRadius = info->si->lmFilterRadius;
1172                 VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
1173                 lm->floodlightDistance = info->si->floodlightDistance;
1174                 lm->floodlightIntensity = info->si->floodlightIntensity;
1175                 lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
1176                 VectorCopy( info->axis, lm->axis );
1177                 lm->plane = info->plane;
1178                 VectorCopy( info->mins, lm->mins );
1179                 VectorCopy( info->maxs, lm->maxs );
1180
1181                 lm->customWidth = info->si->lmCustomWidth;
1182                 lm->customHeight = info->si->lmCustomHeight;
1183
1184                 /* add the surface to the raw lightmap */
1185                 AddSurfaceToRawLightmap( num, lm );
1186                 info->lm = lm;
1187
1188                 /* do an exhaustive merge */
1189                 added = qtrue;
1190                 while ( added )
1191                 {
1192                         /* walk the list of surfaces again */
1193                         added = qfalse;
1194                         for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1195                         {
1196                                 /* get info and attempt early out */
1197                                 num2 = sortSurfaces[ j ];
1198                                 info2 = &surfaceInfos[ num2 ];
1199                                 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1200                                         continue;
1201                                 }
1202
1203                                 /* add the surface to the raw lightmap */
1204                                 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1205                                         info2->lm = lm;
1206                                         added = qtrue;
1207                                 }
1208                                 else
1209                                 {
1210                                         /* back up one */
1211                                         lm->numLightSurfaces--;
1212                                         numLightSurfaces--;
1213                                 }
1214                         }
1215                 }
1216
1217                 /* finish the lightmap and allocate the various buffers */
1218                 FinishRawLightmap( lm );
1219         }
1220
1221         if ( debugSampleSize < -1 ){
1222                 Sys_FPrintf( SYS_VRB, "+%d similar occurrences;\t-debugSampleSize to show ones\n", -debugSampleSize - 1 );
1223         }
1224
1225         /* allocate vertex luxel storage */
1226         for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1227         {
1228                 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1229                 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1230                 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1231                 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1232         }
1233
1234         /* emit some stats */
1235         Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1236         Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1237         Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1238         Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1239         Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1240         Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1241         Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1242         Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1243 }
1244
1245
1246
1247 /*
1248    StitchSurfaceLightmaps()
1249    stitches lightmap edges
1250    2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1251  */
1252
1253 #define MAX_STITCH_CANDIDATES   32
1254 #define MAX_STITCH_LUXELS       64
1255
1256 void StitchSurfaceLightmaps( void ){
1257         int i, j, x, y, x2, y2, *cluster, *cluster2,
1258                 numStitched, numCandidates, numLuxels, f, fOld, start;
1259         rawLightmap_t   *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1260         float           *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1261                                          sampleSize, average[ 3 ], totalColor, ootc;
1262
1263
1264         /* disabled for now */
1265         return;
1266
1267         /* note it */
1268         Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1269
1270         /* init pacifier */
1271         fOld = -1;
1272         start = I_FloatTime();
1273
1274         /* walk the list of raw lightmaps */
1275         numStitched = 0;
1276         for ( i = 0; i < numRawLightmaps; i++ )
1277         {
1278                 /* print pacifier */
1279                 f = 10 * i / numRawLightmaps;
1280                 if ( f != fOld ) {
1281                         fOld = f;
1282                         Sys_Printf( "%i...", f );
1283                 }
1284
1285                 /* get lightmap a */
1286                 a = &rawLightmaps[ i ];
1287
1288                 /* walk rest of lightmaps */
1289                 numCandidates = 0;
1290                 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1291                 {
1292                         /* get lightmap b */
1293                         b = &rawLightmaps[ j ];
1294
1295                         /* test bounding box */
1296                         if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1297                                  a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1298                                  a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1299                                 continue;
1300                         }
1301
1302                         /* add candidate */
1303                         c[ numCandidates++ ] = b;
1304                 }
1305
1306                 /* walk luxels */
1307                 for ( y = 0; y < a->sh; y++ )
1308                 {
1309                         for ( x = 0; x < a->sw; x++ )
1310                         {
1311                                 /* ignore unmapped/unlit luxels */
1312                                 lm = a;
1313                                 cluster = SUPER_CLUSTER( x, y );
1314                                 if ( *cluster == CLUSTER_UNMAPPED ) {
1315                                         continue;
1316                                 }
1317                                 luxel = SUPER_LUXEL( 0, x, y );
1318                                 if ( luxel[ 3 ] <= 0.0f ) {
1319                                         continue;
1320                                 }
1321
1322                                 /* get particulars */
1323                                 origin = SUPER_ORIGIN( x, y );
1324                                 normal = SUPER_NORMAL( x, y );
1325
1326                                 /* walk candidate list */
1327                                 for ( j = 0; j < numCandidates; j++ )
1328                                 {
1329                                         /* get candidate */
1330                                         b = c[ j ];
1331                                         lm = b;
1332
1333                                         /* set samplesize to the smaller of the pair */
1334                                         sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1335
1336                                         /* test bounding box */
1337                                         if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1338                                                  origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1339                                                  origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1340                                                 continue;
1341                                         }
1342
1343                                         /* walk candidate luxels */
1344                                         VectorClear( average );
1345                                         numLuxels = 0;
1346                                         totalColor = 0.0f;
1347                                         for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1348                                         {
1349                                                 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1350                                                 {
1351                                                         /* ignore same luxels */
1352                                                         if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1353                                                                 continue;
1354                                                         }
1355
1356                                                         /* ignore unmapped/unlit luxels */
1357                                                         cluster2 = SUPER_CLUSTER( x2, y2 );
1358                                                         if ( *cluster2 == CLUSTER_UNMAPPED ) {
1359                                                                 continue;
1360                                                         }
1361                                                         luxel2 = SUPER_LUXEL( 0, x2, y2 );
1362                                                         if ( luxel2[ 3 ] <= 0.0f ) {
1363                                                                 continue;
1364                                                         }
1365
1366                                                         /* get particulars */
1367                                                         origin2 = SUPER_ORIGIN( x2, y2 );
1368                                                         normal2 = SUPER_NORMAL( x2, y2 );
1369
1370                                                         /* test normal */
1371                                                         if ( DotProduct( normal, normal2 ) < 0.5f ) {
1372                                                                 continue;
1373                                                         }
1374
1375                                                         /* test bounds */
1376                                                         if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1377                                                                  fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1378                                                                  fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1379                                                                 continue;
1380                                                         }
1381
1382                                                         /* add luxel */
1383                                                         //%     VectorSet( luxel2, 255, 0, 255 );
1384                                                         VectorAdd( average, luxel2, average );
1385                                                         totalColor += luxel2[ 3 ];
1386                                                 }
1387                                         }
1388
1389                                         /* early out */
1390                                         if ( numLuxels == 0 ) {
1391                                                 continue;
1392                                         }
1393
1394                                         /* scale average */
1395                                         ootc = 1.0f / totalColor;
1396                                         VectorScale( average, ootc, luxel );
1397                                         luxel[ 3 ] = 1.0f;
1398                                         numStitched++;
1399                                 }
1400                         }
1401                 }
1402         }
1403
1404         /* emit statistics */
1405         Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1406         Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1407 }
1408
1409
1410
1411 /*
1412    CompareBSPLuxels()
1413    compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1414  */
1415
1416 #define SOLID_EPSILON       0.0625
1417 #define LUXEL_TOLERANCE     0.0025
1418 #define LUXEL_COLOR_FRAC    0.001302083 /* 1 / 3 / 256 */
1419
1420 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1421         rawLightmap_t   *lm;
1422         int x, y;
1423         double delta, total, rd, gd, bd;
1424         float           *aLuxel, *bLuxel;
1425
1426
1427         /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1428         if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1429                  ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1430                 return qfalse;
1431         }
1432
1433         /* basic tests */
1434         if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1435                  a->brightness != b->brightness ||
1436                  a->solid[ aNum ] != b->solid[ bNum ] ||
1437                  a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1438                 return qfalse;
1439         }
1440
1441         /* compare solid color lightmaps */
1442         if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1443                 /* get deltas */
1444                 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1445                 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1446                 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1447
1448                 /* compare color */
1449                 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1450                         return qfalse;
1451                 }
1452
1453                 /* okay */
1454                 return qtrue;
1455         }
1456
1457         /* compare nonsolid lightmaps */
1458         if ( a->w != b->w || a->h != b->h ) {
1459                 return qfalse;
1460         }
1461
1462         /* compare luxels */
1463         delta = 0.0;
1464         total = 0.0;
1465         for ( y = 0; y < a->h; y++ )
1466         {
1467                 for ( x = 0; x < a->w; x++ )
1468                 {
1469                         /* increment total */
1470                         total += 1.0;
1471
1472                         /* get luxels */
1473                         lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1474                         lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1475
1476                         /* ignore unused luxels */
1477                         if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1478                                 continue;
1479                         }
1480
1481                         /* get deltas */
1482                         rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1483                         gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1484                         bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1485
1486                         /* 2003-09-27: compare individual luxels */
1487                         if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1488                                 return qfalse;
1489                         }
1490
1491                         /* compare (fixme: take into account perceptual differences) */
1492                         delta += rd * LUXEL_COLOR_FRAC;
1493                         delta += gd * LUXEL_COLOR_FRAC;
1494                         delta += bd * LUXEL_COLOR_FRAC;
1495
1496                         /* is the change too high? */
1497                         if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1498                                 return qfalse;
1499                         }
1500                 }
1501         }
1502
1503         /* made it this far, they must be identical (or close enough) */
1504         return qtrue;
1505 }
1506
1507
1508
1509 /*
1510    MergeBSPLuxels()
1511    merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1512  */
1513
1514 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1515         rawLightmap_t   *lm;
1516         int x, y;
1517         float luxel[ 3 ], *aLuxel, *bLuxel;
1518
1519
1520         /* basic tests */
1521         if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1522                  a->brightness != b->brightness ||
1523                  a->solid[ aNum ] != b->solid[ bNum ] ||
1524                  a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1525                 return qfalse;
1526         }
1527
1528         /* compare solid lightmaps */
1529         if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1530                 /* average */
1531                 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1532                 VectorScale( luxel, 0.5f, luxel );
1533
1534                 /* copy to both */
1535                 VectorCopy( luxel, a->solidColor[ aNum ] );
1536                 VectorCopy( luxel, b->solidColor[ bNum ] );
1537
1538                 /* return to sender */
1539                 return qtrue;
1540         }
1541
1542         /* compare nonsolid lightmaps */
1543         if ( a->w != b->w || a->h != b->h ) {
1544                 return qfalse;
1545         }
1546
1547         /* merge luxels */
1548         for ( y = 0; y < a->h; y++ )
1549         {
1550                 for ( x = 0; x < a->w; x++ )
1551                 {
1552                         /* get luxels */
1553                         lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1554                         lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1555
1556                         /* handle occlusion mismatch */
1557                         if ( aLuxel[ 0 ] < 0.0f ) {
1558                                 VectorCopy( bLuxel, aLuxel );
1559                         }
1560                         else if ( bLuxel[ 0 ] < 0.0f ) {
1561                                 VectorCopy( aLuxel, bLuxel );
1562                         }
1563                         else
1564                         {
1565                                 /* average */
1566                                 VectorAdd( aLuxel, bLuxel, luxel );
1567                                 VectorScale( luxel, 0.5f, luxel );
1568
1569                                 /* debugging code */
1570                                 //%     luxel[ 2 ] += 64.0f;
1571
1572                                 /* copy to both */
1573                                 VectorCopy( luxel, aLuxel );
1574                                 VectorCopy( luxel, bLuxel );
1575                         }
1576                 }
1577         }
1578
1579         /* done */
1580         return qtrue;
1581 }
1582
1583
1584
1585 /*
1586    ApproximateLuxel()
1587    determines if a single luxel is can be approximated with the interpolated vertex rgba
1588  */
1589
1590 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1591         int i, x, y, d, lightmapNum;
1592         float   *luxel;
1593         vec3_t color, vertexColor;
1594         byte cb[ 4 ], vcb[ 4 ];
1595
1596
1597         /* find luxel xy coords */
1598         x = dv->lightmap[ 0 ][ 0 ] / superSample;
1599         y = dv->lightmap[ 0 ][ 1 ] / superSample;
1600         if ( x < 0 ) {
1601                 x = 0;
1602         }
1603         else if ( x >= lm->w ) {
1604                 x = lm->w - 1;
1605         }
1606         if ( y < 0 ) {
1607                 y = 0;
1608         }
1609         else if ( y >= lm->h ) {
1610                 y = lm->h - 1;
1611         }
1612
1613         /* walk list */
1614         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1615         {
1616                 /* early out */
1617                 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1618                         continue;
1619                 }
1620
1621                 /* get luxel */
1622                 luxel = BSP_LUXEL( lightmapNum, x, y );
1623
1624                 /* ignore occluded luxels */
1625                 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1626                         return qtrue;
1627                 }
1628
1629                 /* copy, set min color and compare */
1630                 VectorCopy( luxel, color );
1631                 VectorCopy( dv->color[ 0 ], vertexColor );
1632
1633                 /* styles are not affected by minlight */
1634                 if ( lightmapNum == 0 ) {
1635                         for ( i = 0; i < 3; i++ )
1636                         {
1637                                 /* set min color */
1638                                 if ( color[ i ] < minLight[ i ] ) {
1639                                         color[ i ] = minLight[ i ];
1640                                 }
1641                                 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1642                                         vertexColor[ i ] = minLight[ i ];
1643                                 }
1644                         }
1645                 }
1646
1647                 /* set to bytes */
1648                 ColorToBytes( color, cb, 1.0f );
1649                 ColorToBytes( vertexColor, vcb, 1.0f );
1650
1651                 /* compare */
1652                 for ( i = 0; i < 3; i++ )
1653                 {
1654                         d = cb[ i ] - vcb[ i ];
1655                         if ( d < 0 ) {
1656                                 d *= -1;
1657                         }
1658                         if ( d > approximateTolerance ) {
1659                                 return qfalse;
1660                         }
1661                 }
1662         }
1663
1664         /* close enough for the girls i date */
1665         return qtrue;
1666 }
1667
1668
1669
1670 /*
1671    ApproximateTriangle()
1672    determines if a single triangle can be approximated with vertex rgba
1673  */
1674
1675 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1676         bspDrawVert_t mid, *dv2[ 3 ];
1677         int max;
1678
1679
1680         /* approximate the vertexes */
1681         if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1682                 return qfalse;
1683         }
1684         if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1685                 return qfalse;
1686         }
1687         if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1688                 return qfalse;
1689         }
1690
1691         /* subdivide calc */
1692         {
1693                 int i;
1694                 float dx, dy, dist, maxDist;
1695
1696
1697                 /* find the longest edge and split it */
1698                 max = -1;
1699                 maxDist = 0;
1700                 for ( i = 0; i < 3; i++ )
1701                 {
1702                         dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1703                         dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1704                         dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1705                         if ( dist > maxDist ) {
1706                                 maxDist = dist;
1707                                 max = i;
1708                         }
1709                 }
1710
1711                 /* try to early out */
1712                 if ( i < 0 || maxDist < subdivideThreshold ) {
1713                         return qtrue;
1714                 }
1715         }
1716
1717         /* split the longest edge and map it */
1718         LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1719         if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1720                 return qfalse;
1721         }
1722
1723         /* recurse to first triangle */
1724         VectorCopy( dv, dv2 );
1725         dv2[ max ] = &mid;
1726         if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1727                 return qfalse;
1728         }
1729
1730         /* recurse to second triangle */
1731         VectorCopy( dv, dv2 );
1732         dv2[ ( max + 1 ) % 3 ] = &mid;
1733         return ApproximateTriangle_r( lm, dv2 );
1734 }
1735
1736
1737
1738 /*
1739    ApproximateLightmap()
1740    determines if a raw lightmap can be approximated sufficiently with vertex colors
1741  */
1742
1743 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1744         int n, num, i, x, y, pw[ 5 ], r;
1745         bspDrawSurface_t    *ds;
1746         surfaceInfo_t       *info;
1747         mesh_t src, *subdivided, *mesh;
1748         bspDrawVert_t       *verts, *dv[ 3 ];
1749         qboolean approximated;
1750
1751
1752         /* approximating? */
1753         if ( approximateTolerance <= 0 ) {
1754                 return qfalse;
1755         }
1756
1757         /* test for jmonroe */
1758         #if 0
1759         /* don't approx lightmaps with styled twins */
1760         if ( lm->numStyledTwins > 0 ) {
1761                 return qfalse;
1762         }
1763
1764         /* don't approx lightmaps with styles */
1765         for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1766         {
1767                 if ( lm->styles[ i ] != LS_NONE ) {
1768                         return qfalse;
1769                 }
1770         }
1771         #endif
1772
1773         /* assume reduced until shadow detail is found */
1774         approximated = qtrue;
1775
1776         /* walk the list of surfaces on this raw lightmap */
1777         for ( n = 0; n < lm->numLightSurfaces; n++ )
1778         {
1779                 /* get surface */
1780                 num = lightSurfaces[ lm->firstLightSurface + n ];
1781                 ds = &bspDrawSurfaces[ num ];
1782                 info = &surfaceInfos[ num ];
1783
1784                 /* assume not-reduced initially */
1785                 info->approximated = qfalse;
1786
1787                 /* bail if lightmap doesn't match up */
1788                 if ( info->lm != lm ) {
1789                         continue;
1790                 }
1791
1792                 /* bail if not vertex lit */
1793                 if ( info->si->noVertexLight ) {
1794                         continue;
1795                 }
1796
1797                 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1798                 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1799                          ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1800                          ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1801                         info->approximated = qtrue;
1802                         numSurfsVertexForced++;
1803                         continue;
1804                 }
1805
1806                 /* handle the triangles */
1807                 switch ( ds->surfaceType )
1808                 {
1809                 case MST_PLANAR:
1810                         /* get verts */
1811                         verts = yDrawVerts + ds->firstVert;
1812
1813                         /* map the triangles */
1814                         info->approximated = qtrue;
1815                         for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1816                         {
1817                                 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1818                                 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1819                                 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1820                                 info->approximated = ApproximateTriangle_r( lm, dv );
1821                         }
1822                         break;
1823
1824                 case MST_PATCH:
1825                         /* make a mesh from the drawsurf */
1826                         src.width = ds->patchWidth;
1827                         src.height = ds->patchHeight;
1828                         src.verts = &yDrawVerts[ ds->firstVert ];
1829                         //%     subdivided = SubdivideMesh( src, 8, 512 );
1830                         subdivided = SubdivideMesh2( src, info->patchIterations );
1831
1832                         /* fit it to the curve and remove colinear verts on rows/columns */
1833                         PutMeshOnCurve( *subdivided );
1834                         mesh = RemoveLinearMeshColumnsRows( subdivided );
1835                         FreeMesh( subdivided );
1836
1837                         /* get verts */
1838                         verts = mesh->verts;
1839
1840                         /* map the mesh quads */
1841                         info->approximated = qtrue;
1842                         for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1843                         {
1844                                 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1845                                 {
1846                                         /* set indexes */
1847                                         pw[ 0 ] = x + ( y * mesh->width );
1848                                         pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1849                                         pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1850                                         pw[ 3 ] = x + 1 + ( y * mesh->width );
1851                                         pw[ 4 ] = x + ( y * mesh->width );      /* same as pw[ 0 ] */
1852
1853                                         /* set radix */
1854                                         r = ( x + y ) & 1;
1855
1856                                         /* get drawverts and map first triangle */
1857                                         dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1858                                         dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1859                                         dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1860                                         info->approximated = ApproximateTriangle_r( lm, dv );
1861
1862                                         /* get drawverts and map second triangle */
1863                                         dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1864                                         dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1865                                         dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1866                                         if ( info->approximated ) {
1867                                                 info->approximated = ApproximateTriangle_r( lm, dv );
1868                                         }
1869                                 }
1870                         }
1871
1872                         /* free the mesh */
1873                         FreeMesh( mesh );
1874                         break;
1875
1876                 default:
1877                         break;
1878                 }
1879
1880                 /* reduced? */
1881                 if ( info->approximated == qfalse ) {
1882                         approximated = qfalse;
1883                 }
1884                 else{
1885                         numSurfsVertexApproximated++;
1886                 }
1887         }
1888
1889         /* return */
1890         return approximated;
1891 }
1892
1893
1894
1895 /*
1896    TestOutLightmapStamp()
1897    tests a stamp on a given lightmap for validity
1898  */
1899
1900 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1901         int sx, sy, ox, oy, offset;
1902         float       *luxel;
1903
1904
1905         /* bounds check */
1906         if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1907                 return qfalse;
1908         }
1909
1910         /* solid lightmaps test a 1x1 stamp */
1911         if ( lm->solid[ lightmapNum ] ) {
1912                 offset = ( y * olm->customWidth ) + x;
1913                 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1914                         return qfalse;
1915                 }
1916                 return qtrue;
1917         }
1918
1919         /* test the stamp */
1920         for ( sy = 0; sy < lm->h; sy++ )
1921         {
1922                 for ( sx = 0; sx < lm->w; sx++ )
1923                 {
1924                         /* get luxel */
1925                         luxel = BSP_LUXEL( lightmapNum, sx, sy );
1926                         if ( luxel[ 0 ] < 0.0f ) {
1927                                 continue;
1928                         }
1929
1930                         /* get bsp lightmap coords and test */
1931                         ox = x + sx;
1932                         oy = y + sy;
1933                         offset = ( oy * olm->customWidth ) + ox;
1934                         if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1935                                 return qfalse;
1936                         }
1937                 }
1938         }
1939
1940         /* stamp is empty */
1941         return qtrue;
1942 }
1943
1944
1945
1946 /*
1947    SetupOutLightmap()
1948    sets up an output lightmap
1949  */
1950
1951 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1952         /* dummy check */
1953         if ( lm == NULL || olm == NULL ) {
1954                 return;
1955         }
1956
1957         /* is this a "normal" bsp-stored lightmap? */
1958         if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1959                 olm->lightmapNum = numBSPLightmaps;
1960                 numBSPLightmaps++;
1961
1962                 /* lightmaps are interleaved with light direction maps */
1963                 if ( deluxemap ) {
1964                         numBSPLightmaps++;
1965                 }
1966         }
1967         else{
1968                 olm->lightmapNum = -3;
1969         }
1970
1971         /* set external lightmap number */
1972         olm->extLightmapNum = -1;
1973
1974         /* set it up */
1975         olm->numLightmaps = 0;
1976         olm->customWidth = lm->customWidth;
1977         olm->customHeight = lm->customHeight;
1978         olm->freeLuxels = olm->customWidth * olm->customHeight;
1979         olm->numShaders = 0;
1980
1981         /* allocate buffers */
1982         olm->lightBits = safe_malloc( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1983         memset( olm->lightBits, 0, ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1984         olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1985         memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1986         if ( deluxemap ) {
1987                 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1988                 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1989         }
1990 }
1991
1992
1993
1994 /*
1995    FindOutLightmaps()
1996    for a given surface lightmap, find output lightmap pages and positions for it
1997  */
1998
1999 #define LIGHTMAP_RESERVE_COUNT 1
2000 static void FindOutLightmaps( rawLightmap_t *lm, qboolean fastAllocate ){
2001         int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
2002         outLightmap_t       *olm;
2003         surfaceInfo_t       *info;
2004         float               *luxel, *deluxel;
2005         vec3_t color, direction;
2006         byte                *pixel;
2007         qboolean ok;
2008         int xIncrement, yIncrement;
2009
2010
2011         /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
2012         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2013                 lm->outLightmapNums[ lightmapNum ] = -3;
2014
2015         /* can this lightmap be approximated with vertex color? */
2016         if ( ApproximateLightmap( lm ) ) {
2017                 return;
2018         }
2019
2020         /* walk list */
2021         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2022         {
2023                 /* early out */
2024                 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
2025                         continue;
2026                 }
2027
2028                 /* don't store twinned lightmaps */
2029                 if ( lm->twins[ lightmapNum ] != NULL ) {
2030                         continue;
2031                 }
2032
2033                 /* if this is a styled lightmap, try some normalized locations first */
2034                 ok = qfalse;
2035                 if ( lightmapNum > 0 && outLightmaps != NULL ) {
2036                         /* loop twice */
2037                         for ( j = 0; j < 2; j++ )
2038                         {
2039                                 /* try identical position */
2040                                 for ( i = 0; i < numOutLightmaps; i++ )
2041                                 {
2042                                         /* get the output lightmap */
2043                                         olm = &outLightmaps[ i ];
2044
2045                                         /* simple early out test */
2046                                         if ( olm->freeLuxels < lm->used ) {
2047                                                 continue;
2048                                         }
2049
2050                                         /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2051                                         if ( olm->customWidth != lm->customWidth ||
2052                                                  olm->customHeight != lm->customHeight ) {
2053                                                 continue;
2054                                         }
2055
2056                                         /* try identical */
2057                                         if ( j == 0 ) {
2058                                                 x = lm->lightmapX[ 0 ];
2059                                                 y = lm->lightmapY[ 0 ];
2060                                                 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2061                                         }
2062
2063                                         /* try shifting */
2064                                         else
2065                                         {
2066                                                 for ( sy = -1; sy <= 1; sy++ )
2067                                                 {
2068                                                         for ( sx = -1; sx <= 1; sx++ )
2069                                                         {
2070                                                                 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 );  //%   lm->w;
2071                                                                 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //%   lm->h;
2072                                                                 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2073
2074                                                                 if ( ok ) {
2075                                                                         break;
2076                                                                 }
2077                                                         }
2078
2079                                                         if ( ok ) {
2080                                                                 break;
2081                                                         }
2082                                                 }
2083                                         }
2084
2085                                         if ( ok ) {
2086                                                 break;
2087                                         }
2088                                 }
2089
2090                                 if ( ok ) {
2091                                         break;
2092                                 }
2093                         }
2094                 }
2095
2096                 /* try normal placement algorithm */
2097                 if ( ok == qfalse ) {
2098                         /* reset origin */
2099                         x = 0;
2100                         y = 0;
2101
2102                         /* walk the list of lightmap pages */
2103                         if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
2104                                 i = 0;
2105                         }
2106                         else{
2107                                 i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
2108                         }
2109                         for ( ; i < numOutLightmaps; i++ )
2110                         {
2111                                 /* get the output lightmap */
2112                                 olm = &outLightmaps[ i ];
2113
2114                                 /* simple early out test */
2115                                 if ( olm->freeLuxels < lm->used ) {
2116                                         continue;
2117                                 }
2118
2119                                 /* if fast allocation, skip lightmap files that are more than 90% complete */
2120                                 if ( fastAllocate == qtrue ) {
2121                                         if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
2122                                                 continue;
2123                                         }
2124                                 }
2125
2126                                 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2127                                 if ( olm->customWidth != lm->customWidth ||
2128                                          olm->customHeight != lm->customHeight ) {
2129                                         continue;
2130                                 }
2131
2132                                 /* set maxs */
2133                                 if ( lm->solid[ lightmapNum ] ) {
2134                                         xMax = olm->customWidth;
2135                                         yMax = olm->customHeight;
2136                                 }
2137                                 else
2138                                 {
2139                                         xMax = ( olm->customWidth - lm->w ) + 1;
2140                                         yMax = ( olm->customHeight - lm->h ) + 1;
2141                                 }
2142
2143                                 /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
2144                                 if ( fastAllocate == qtrue ) {
2145                                         xIncrement = MAX(1, lm->w / 15);
2146                                         yIncrement = MAX(1, lm->h / 15);
2147                                 }
2148                                 else {
2149                                         xIncrement = 1;
2150                                         yIncrement = 1;
2151                                 }
2152
2153                                 /* walk the origin around the lightmap */
2154                                 for ( y = 0; y < yMax; y += yIncrement )
2155                                 {
2156                                         for ( x = 0; x < xMax; x += xIncrement )
2157                                         {
2158                                                 /* find a fine tract of lauhnd */
2159                                                 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2160
2161                                                 if ( ok ) {
2162                                                         break;
2163                                                 }
2164                                         }
2165
2166                                         if ( ok ) {
2167                                                 break;
2168                                         }
2169                                 }
2170
2171                                 if ( ok ) {
2172                                         break;
2173                                 }
2174
2175                                 /* reset x and y */
2176                                 x = 0;
2177                                 y = 0;
2178                         }
2179                 }
2180
2181                 /* no match? */
2182                 if ( ok == qfalse ) {
2183                         /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2184                         numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2185                         olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2186                         if ( !olm ){
2187                                 Error( "FindOutLightmaps: Failed to allocate memory.\n" );
2188                         }
2189
2190                         if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2191                                 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2192                                 free( outLightmaps );
2193                         }
2194                         outLightmaps = olm;
2195
2196                         /* initialize both out lightmaps */
2197                         for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2198                                 SetupOutLightmap( lm, &outLightmaps[ k ] );
2199
2200                         /* set out lightmap */
2201                         i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2202                         olm = &outLightmaps[ i ];
2203
2204                         /* set stamp xy origin to the first surface lightmap */
2205                         if ( lightmapNum > 0 ) {
2206                                 x = lm->lightmapX[ 0 ];
2207                                 y = lm->lightmapY[ 0 ];
2208                         }
2209                 }
2210
2211                 /* if this is a style-using lightmap, it must be exported */
2212                 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2213                         olm->extLightmapNum = 0;
2214                 }
2215
2216                 /* add the surface lightmap to the bsp lightmap */
2217                 lm->outLightmapNums[ lightmapNum ] = i;
2218                 lm->lightmapX[ lightmapNum ] = x;
2219                 lm->lightmapY[ lightmapNum ] = y;
2220                 olm->numLightmaps++;
2221
2222                 /* add shaders */
2223                 for ( i = 0; i < lm->numLightSurfaces; i++ )
2224                 {
2225                         /* get surface info */
2226                         info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2227
2228                         /* test for shader */
2229                         for ( j = 0; j < olm->numShaders; j++ )
2230                         {
2231                                 if ( olm->shaders[ j ] == info->si ) {
2232                                         break;
2233                                 }
2234                         }
2235
2236                         /* if it doesn't exist, add it */
2237                         if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2238                                 olm->shaders[ olm->numShaders ] = info->si;
2239                                 olm->numShaders++;
2240                                 numLightmapShaders++;
2241                         }
2242                 }
2243
2244                 /* set maxs */
2245                 if ( lm->solid[ lightmapNum ] ) {
2246                         xMax = 1;
2247                         yMax = 1;
2248                 }
2249                 else
2250                 {
2251                         xMax = lm->w;
2252                         yMax = lm->h;
2253                 }
2254
2255                 /* mark the bits used */
2256                 for ( y = 0; y < yMax; y++ )
2257                 {
2258                         for ( x = 0; x < xMax; x++ )
2259                         {
2260                                 /* get luxel */
2261                                 luxel = BSP_LUXEL( lightmapNum, x, y );
2262                                 deluxel = BSP_DELUXEL( x, y );
2263                                 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2264                                         continue;
2265                                 }
2266
2267                                 /* set minimum light */
2268                                 if ( lm->solid[ lightmapNum ] ) {
2269                                         if ( debug ) {
2270                                                 VectorSet( color, 255.0f, 0.0f, 0.0f );
2271                                         }
2272                                         else{
2273                                                 VectorCopy( lm->solidColor[ lightmapNum ], color );
2274                                         }
2275                                 }
2276                                 else{
2277                                         VectorCopy( luxel, color );
2278                                 }
2279
2280                                 /* styles are not affected by minlight */
2281                                 if ( lightmapNum == 0 ) {
2282                                         for ( i = 0; i < 3; i++ )
2283                                         {
2284                                                 if ( color[ i ] < minLight[ i ] ) {
2285                                                         color[ i ] = minLight[ i ];
2286                                                 }
2287                                         }
2288                                 }
2289
2290                                 /* get bsp lightmap coords  */
2291                                 ox = x + lm->lightmapX[ lightmapNum ];
2292                                 oy = y + lm->lightmapY[ lightmapNum ];
2293                                 offset = ( oy * olm->customWidth ) + ox;
2294
2295                                 /* flag pixel as used */
2296                                 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2297                                 olm->freeLuxels--;
2298
2299                                 /* store color */
2300                                 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2301                                 ColorToBytes( color, pixel, lm->brightness );
2302
2303                                 /* store direction */
2304                                 if ( deluxemap ) {
2305                                         /* normalize average light direction */
2306                                         pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2307                                         VectorScale( deluxel, 1000.0f, direction );
2308                                         VectorNormalize( direction, direction );
2309                                         VectorScale( direction, 127.5f, direction );
2310                                         for ( i = 0; i < 3; i++ )
2311                                                 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2312                                 }
2313                         }
2314                 }
2315         }
2316 }
2317
2318
2319
2320 /*
2321    CompareRawLightmap()
2322    compare function for qsort()
2323  */
2324
2325 static int CompareRawLightmap( const void *a, const void *b ){
2326         rawLightmap_t   *alm, *blm;
2327         surfaceInfo_t   *aInfo, *bInfo;
2328         int i, min, diff;
2329
2330
2331         /* get lightmaps */
2332         alm = &rawLightmaps[ *( (const int*) a ) ];
2333         blm = &rawLightmaps[ *( (const int*) b ) ];
2334
2335         /* get min number of surfaces */
2336         min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2337
2338 //#define allocate_bigger_first
2339 #ifdef allocate_bigger_first
2340         /* compare size, allocate bigger first */
2341         // fastAllocate commit part: can kick fps by unique lightmap/shader combinations*=~2 + bigger compile time
2342         //return -diff; makes packing faster and rough
2343         diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2344         if ( diff != 0 ) {
2345                 return diff;
2346         }
2347 #endif
2348         /* iterate */
2349         for ( i = 0; i < min; i++ )
2350         {
2351                 /* get surface info */
2352                 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2353                 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2354
2355                 /* compare shader names */
2356                 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2357                 if ( diff != 0 ) {
2358                         return diff;
2359                 }
2360         }
2361
2362         /* test style count */
2363         diff = 0;
2364         for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2365                 diff += blm->styles[ i ] - alm->styles[ i ];
2366         if ( diff ) {
2367                 return diff;
2368         }
2369 #ifndef allocate_bigger_first
2370         /* compare size */
2371         diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2372         if ( diff != 0 ) {
2373                 return diff;
2374         }
2375 #endif
2376         /* must be equivalent */
2377         return 0;
2378 }
2379
2380
2381
2382 void FillOutLightmap( outLightmap_t *olm ){
2383         int x, y;
2384         int ofs;
2385         vec3_t dir_sum, light_sum;
2386         int cnt, filled;
2387         byte *lightBitsNew = NULL;
2388         byte *lightBytesNew = NULL;
2389         byte *dirBytesNew = NULL;
2390
2391         lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2392         lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2393         if ( deluxemap ) {
2394                 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2395         }
2396
2397         /*
2398            memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2399             olm->lightBits[0] |= 1;
2400             olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2401            memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2402             olm->bspLightBytes[0] = 255;
2403             olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2404          */
2405
2406         memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2407         memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2408         if ( deluxemap ) {
2409                 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2410         }
2411
2412         for (;; )
2413         {
2414                 filled = 0;
2415                 for ( y = 0; y < olm->customHeight; ++y )
2416                 {
2417                         for ( x = 0; x < olm->customWidth; ++x )
2418                         {
2419                                 ofs = y * olm->customWidth + x;
2420                                 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2421                                         continue;
2422                                 }
2423                                 cnt = 0;
2424                                 VectorClear( dir_sum );
2425                                 VectorClear( light_sum );
2426
2427                                 /* try all four neighbors */
2428                                 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2429                                 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2430                                         ++cnt;
2431                                         VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2432                                         if ( deluxemap ) {
2433                                                 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2434                                         }
2435                                 }
2436
2437                                 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2438                                 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2439                                         ++cnt;
2440                                         VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2441                                         if ( deluxemap ) {
2442                                                 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2443                                         }
2444                                 }
2445
2446                                 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2447                                 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2448                                         ++cnt;
2449                                         VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2450                                         if ( deluxemap ) {
2451                                                 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2452                                         }
2453                                 }
2454
2455                                 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2456                                 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2457                                         ++cnt;
2458                                         VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2459                                         if ( deluxemap ) {
2460                                                 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2461                                         }
2462                                 }
2463
2464                                 if ( cnt ) {
2465                                         ++filled;
2466                                         ofs = y * olm->customWidth + x;
2467                                         lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2468                                         VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2469                                         if ( deluxemap ) {
2470                                                 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2471                                         }
2472                                 }
2473                         }
2474                 }
2475
2476                 if ( !filled ) {
2477                         break;
2478                 }
2479
2480                 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2481                 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2482                 if ( deluxemap ) {
2483                         memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2484                 }
2485         }
2486
2487         free( lightBitsNew );
2488         free( lightBytesNew );
2489         if ( deluxemap ) {
2490                 free( dirBytesNew );
2491         }
2492 }
2493
2494 /*
2495    StoreSurfaceLightmaps()
2496    stores the surface lightmaps into the bsp as byte rgb triplets
2497  */
2498
2499 void StoreSurfaceLightmaps( qboolean fastAllocate ){
2500         int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples, timer_start;
2501         int style, size, lightmapNum, lightmapNum2;
2502         float               *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2503         vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2504         float               *deluxel, *bspDeluxel, *bspDeluxel2;
2505         byte                *lb;
2506         int numUsed, numTwins, numTwinLuxels, numStored;
2507         float lmx, lmy, efficiency;
2508         vec3_t color;
2509         bspDrawSurface_t    *ds, *parent, dsTemp;
2510         surfaceInfo_t       *info;
2511         rawLightmap_t       *lm, *lm2;
2512         outLightmap_t       *olm;
2513         bspDrawVert_t       *dv, *ydv, *dvParent;
2514         char dirname[ 1024 ], filename[ 1024 ];
2515         shaderInfo_t        *csi;
2516         char lightmapName[ 128 ];
2517         const char          *rgbGenValues[ 256 ];
2518         const char          *alphaGenValues[ 256 ];
2519
2520
2521         /* note it */
2522         Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2523
2524         /* setup */
2525         if ( lmCustomDir ) {
2526                 strcpy( dirname, lmCustomDir );
2527         }
2528         else
2529         {
2530                 strcpy( dirname, source );
2531                 StripExtension( dirname );
2532         }
2533         memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2534         memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2535
2536         /* -----------------------------------------------------------------
2537            average the sampled luxels into the bsp luxels
2538            ----------------------------------------------------------------- */
2539
2540         /* note it */
2541         Sys_FPrintf( SYS_VRB, "Subsampling..." );
2542
2543         timer_start = I_FloatTime();
2544
2545         /* walk the list of raw lightmaps */
2546         numUsed = 0;
2547         numTwins = 0;
2548         numTwinLuxels = 0;
2549         numSolidLightmaps = 0;
2550         for ( i = 0; i < numRawLightmaps; i++ )
2551         {
2552                 /* get lightmap */
2553                 lm = &rawLightmaps[ i ];
2554
2555                 /* walk individual lightmaps */
2556                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2557                 {
2558                         /* early outs */
2559                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2560                                 continue;
2561                         }
2562
2563                         /* allocate bsp luxel storage */
2564                         if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2565                                 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2566                                 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2567                                 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2568                         }
2569
2570                         /* allocate radiosity lightmap storage */
2571                         if ( bounce ) {
2572                                 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2573                                 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2574                                         lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2575                                 }
2576                                 memset( lm->radLuxels[ lightmapNum ], 0, size );
2577                         }
2578
2579                         /* average supersampled luxels */
2580                         for ( y = 0; y < lm->h; y++ )
2581                         {
2582                                 for ( x = 0; x < lm->w; x++ )
2583                                 {
2584                                         /* subsample */
2585                                         samples = 0.0f;
2586                                         occludedSamples = 0.0f;
2587                                         mappedSamples = 0;
2588                                         VectorClear( sample );
2589                                         VectorClear( occludedSample );
2590                                         VectorClear( dirSample );
2591                                         for ( ly = 0; ly < superSample; ly++ )
2592                                         {
2593                                                 for ( lx = 0; lx < superSample; lx++ )
2594                                                 {
2595                                                         /* sample luxel */
2596                                                         sx = x * superSample + lx;
2597                                                         sy = y * superSample + ly;
2598                                                         luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2599                                                         deluxel = SUPER_DELUXEL( sx, sy );
2600                                                         normal = SUPER_NORMAL( sx, sy );
2601                                                         cluster = SUPER_CLUSTER( sx, sy );
2602
2603                                                         /* sample deluxemap */
2604                                                         if ( deluxemap && lightmapNum == 0 ) {
2605                                                                 VectorAdd( dirSample, deluxel, dirSample );
2606                                                         }
2607
2608                                                         /* keep track of used/occluded samples */
2609                                                         if ( *cluster != CLUSTER_UNMAPPED ) {
2610                                                                 mappedSamples++;
2611                                                         }
2612
2613                                                         /* handle lightmap border? */
2614                                                         if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2615                                                                 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2616                                                                 samples += 1.0f;
2617                                                         }
2618
2619                                                         /* handle debug */
2620                                                         else if ( debug && *cluster < 0 ) {
2621                                                                 if ( *cluster == CLUSTER_UNMAPPED ) {
2622                                                                         VectorSet( luxel, 255, 204, 0 );
2623                                                                 }
2624                                                                 else if ( *cluster == CLUSTER_OCCLUDED ) {
2625                                                                         VectorSet( luxel, 255, 0, 255 );
2626                                                                 }
2627                                                                 else if ( *cluster == CLUSTER_FLOODED ) {
2628                                                                         VectorSet( luxel, 0, 32, 255 );
2629                                                                 }
2630                                                                 VectorAdd( occludedSample, luxel, occludedSample );
2631                                                                 occludedSamples += 1.0f;
2632                                                         }
2633
2634                                                         /* normal luxel handling */
2635                                                         else if ( luxel[ 3 ] > 0.0f ) {
2636                                                                 /* handle lit or flooded luxels */
2637                                                                 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2638                                                                         VectorAdd( sample, luxel, sample );
2639                                                                         samples += luxel[ 3 ];
2640                                                                 }
2641
2642                                                                 /* handle occluded or unmapped luxels */
2643                                                                 else
2644                                                                 {
2645                                                                         VectorAdd( occludedSample, luxel, occludedSample );
2646                                                                         occludedSamples += luxel[ 3 ];
2647                                                                 }
2648
2649                                                                 /* handle style debugging */
2650                                                                 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2651                                                                         VectorCopy( debugColors[ 0 ], sample );
2652                                                                         samples = 1;
2653                                                                 }
2654                                                         }
2655                                                 }
2656                                         }
2657
2658                                         /* only use occluded samples if necessary */
2659                                         if ( samples <= 0.0f ) {
2660                                                 VectorCopy( occludedSample, sample );
2661                                                 samples = occludedSamples;
2662                                         }
2663
2664                                         /* get luxels */
2665                                         luxel = SUPER_LUXEL( lightmapNum, x, y );
2666                                         deluxel = SUPER_DELUXEL( x, y );
2667
2668                                         /* store light direction */
2669                                         if ( deluxemap && lightmapNum == 0 ) {
2670                                                 VectorCopy( dirSample, deluxel );
2671                                         }
2672
2673                                         /* store the sample back in super luxels */
2674                                         if ( samples > 0.01f ) {
2675                                                 VectorScale( sample, ( 1.0f / samples ), luxel );
2676                                                 luxel[ 3 ] = 1.0f;
2677                                         }
2678
2679                                         /* if any samples were mapped in any way, store ambient color */
2680                                         else if ( mappedSamples > 0 ) {
2681                                                 if ( lightmapNum == 0 ) {
2682                                                         VectorCopy( ambientColor, luxel );
2683                                                 }
2684                                                 else{
2685                                                         VectorClear( luxel );
2686                                                 }
2687                                                 luxel[ 3 ] = 1.0f;
2688                                         }
2689
2690                                         /* store a bogus value to be fixed later */
2691                                         else
2692                                         {
2693                                                 VectorClear( luxel );
2694                                                 luxel[ 3 ] = -1.0f;
2695                                         }
2696                                 }
2697                         }
2698
2699                         /* setup */
2700                         lm->used = 0;
2701                         ClearBounds( colorMins, colorMaxs );
2702
2703                         /* clean up and store into bsp luxels */
2704                         for ( y = 0; y < lm->h; y++ )
2705                         {
2706                                 for ( x = 0; x < lm->w; x++ )
2707                                 {
2708                                         /* get luxels */
2709                                         luxel = SUPER_LUXEL( lightmapNum, x, y );
2710                                         deluxel = SUPER_DELUXEL( x, y );
2711
2712                                         /* copy light direction */
2713                                         if ( deluxemap && lightmapNum == 0 ) {
2714                                                 VectorCopy( deluxel, dirSample );
2715                                         }
2716
2717                                         /* is this a valid sample? */
2718                                         if ( luxel[ 3 ] > 0.0f ) {
2719                                                 VectorCopy( luxel, sample );
2720                                                 samples = luxel[ 3 ];
2721                                                 numUsed++;
2722                                                 lm->used++;
2723
2724                                                 /* fix negative samples */
2725                                                 for ( j = 0; j < 3; j++ )
2726                                                 {
2727                                                         if ( sample[ j ] < 0.0f ) {
2728                                                                 sample[ j ] = 0.0f;
2729                                                         }
2730                                                 }
2731                                         }
2732                                         else
2733                                         {
2734                                                 /* nick an average value from the neighbors */
2735                                                 VectorClear( sample );
2736                                                 VectorClear( dirSample );
2737                                                 samples = 0.0f;
2738
2739                                                 /* fixme: why is this disabled?? */
2740                                                 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2741                                                 {
2742                                                         if ( sy < 0 || sy >= lm->h ) {
2743                                                                 continue;
2744                                                         }
2745
2746                                                         for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2747                                                         {
2748                                                                 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2749                                                                         continue;
2750                                                                 }
2751
2752                                                                 /* get neighbor's particulars */
2753                                                                 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2754                                                                 if ( luxel[ 3 ] < 0.0f ) {
2755                                                                         continue;
2756                                                                 }
2757                                                                 VectorAdd( sample, luxel, sample );
2758                                                                 samples += luxel[ 3 ];
2759                                                         }
2760                                                 }
2761
2762                                                 /* no samples? */
2763                                                 if ( samples == 0.0f ) {
2764                                                         VectorSet( sample, -1.0f, -1.0f, -1.0f );
2765                                                         samples = 1.0f;
2766                                                 }
2767                                                 else
2768                                                 {
2769                                                         numUsed++;
2770                                                         lm->used++;
2771
2772                                                         /* fix negative samples */
2773                                                         for ( j = 0; j < 3; j++ )
2774                                                         {
2775                                                                 if ( sample[ j ] < 0.0f ) {
2776                                                                         sample[ j ] = 0.0f;
2777                                                                 }
2778                                                         }
2779                                                 }
2780                                         }
2781
2782                                         /* scale the sample */
2783                                         VectorScale( sample, ( 1.0f / samples ), sample );
2784
2785                                         /* store the sample in the radiosity luxels */
2786                                         if ( bounce > 0 ) {
2787                                                 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2788                                                 VectorCopy( sample, radLuxel );
2789
2790                                                 /* if only storing bounced light, early out here */
2791                                                 if ( bounceOnly && !bouncing ) {
2792                                                         continue;
2793                                                 }
2794                                         }
2795
2796                                         /* store the sample in the bsp luxels */
2797                                         bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2798                                         bspDeluxel = BSP_DELUXEL( x, y );
2799
2800                                         VectorAdd( bspLuxel, sample, bspLuxel );
2801                                         if ( deluxemap && lightmapNum == 0 ) {
2802                                                 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2803                                         }
2804
2805                                         /* add color to bounds for solid checking */
2806                                         if ( samples > 0.0f ) {
2807                                                 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2808                                         }
2809                                 }
2810                         }
2811
2812                         /* set solid color */
2813                         lm->solid[ lightmapNum ] = qfalse;
2814                         VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2815                         VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2816
2817                         /* nocollapse prevents solid lightmaps */
2818                         if ( noCollapse == qfalse ) {
2819                                 /* check solid color */
2820                                 VectorSubtract( colorMaxs, colorMins, sample );
2821                                 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2822                                          ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2823                                         /* set to solid */
2824                                         VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2825                                         lm->solid[ lightmapNum ] = qtrue;
2826                                         numSolidLightmaps++;
2827                                 }
2828
2829                                 /* if all lightmaps aren't solid, then none of them are solid */
2830                                 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2831                                         for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2832                                         {
2833                                                 if ( lm->solid[ y ] ) {
2834                                                         numSolidLightmaps--;
2835                                                 }
2836                                                 lm->solid[ y ] = qfalse;
2837                                         }
2838                                 }
2839                         }
2840
2841                         /* wrap bsp luxels if necessary */
2842                         if ( lm->wrap[ 0 ] ) {
2843                                 for ( y = 0; y < lm->h; y++ )
2844                                 {
2845                                         bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2846                                         bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2847                                         VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2848                                         VectorScale( bspLuxel, 0.5f, bspLuxel );
2849                                         VectorCopy( bspLuxel, bspLuxel2 );
2850                                         if ( deluxemap && lightmapNum == 0 ) {
2851                                                 bspDeluxel = BSP_DELUXEL( 0, y );
2852                                                 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2853                                                 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2854                                                 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2855                                                 VectorCopy( bspDeluxel, bspDeluxel2 );
2856                                         }
2857                                 }
2858                         }
2859                         if ( lm->wrap[ 1 ] ) {
2860                                 for ( x = 0; x < lm->w; x++ )
2861                                 {
2862                                         bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2863                                         bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2864                                         VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2865                                         VectorScale( bspLuxel, 0.5f, bspLuxel );
2866                                         VectorCopy( bspLuxel, bspLuxel2 );
2867                                         if ( deluxemap && lightmapNum == 0 ) {
2868                                                 bspDeluxel = BSP_DELUXEL( x, 0 );
2869                                                 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2870                                                 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2871                                                 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2872                                                 VectorCopy( bspDeluxel, bspDeluxel2 );
2873                                         }
2874                                 }
2875                         }
2876                 }
2877         }
2878
2879         Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
2880
2881         /* -----------------------------------------------------------------
2882            convert modelspace deluxemaps to tangentspace
2883            ----------------------------------------------------------------- */
2884         /* note it */
2885         if ( !bouncing ) {
2886                 if ( deluxemap && deluxemode == 1 ) {
2887                         vec3_t worldUp, myNormal, myTangent, myBinormal;
2888                         float dist;
2889
2890                         timer_start = I_FloatTime();
2891
2892                         Sys_Printf( "converting..." );
2893
2894                         for ( i = 0; i < numRawLightmaps; i++ )
2895                         {
2896                                 /* get lightmap */
2897                                 lm = &rawLightmaps[ i ];
2898
2899                                 /* walk lightmap samples */
2900                                 for ( y = 0; y < lm->sh; y++ )
2901                                 {
2902                                         for ( x = 0; x < lm->sw; x++ )
2903                                         {
2904                                                 /* get normal and deluxel */
2905                                                 normal = SUPER_NORMAL( x, y );
2906                                                 cluster = SUPER_CLUSTER( x, y );
2907                                                 bspDeluxel = BSP_DELUXEL( x, y );
2908                                                 deluxel = SUPER_DELUXEL( x, y );
2909
2910                                                 /* get normal */
2911                                                 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2912
2913                                                 /* get tangent vectors */
2914                                                 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2915                                                         if ( myNormal[ 2 ] == 1.0f ) {
2916                                                                 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2917                                                                 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2918                                                         }
2919                                                         else if ( myNormal[ 2 ] == -1.0f ) {
2920                                                                 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2921                                                                 VectorSet( myBinormal,  0.0f, 1.0f, 0.0f );
2922                                                         }
2923                                                 }
2924                                                 else
2925                                                 {
2926                                                         VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2927                                                         CrossProduct( myNormal, worldUp, myTangent );
2928                                                         VectorNormalize( myTangent, myTangent );
2929                                                         CrossProduct( myTangent, myNormal, myBinormal );
2930                                                         VectorNormalize( myBinormal, myBinormal );
2931                                                 }
2932
2933                                                 /* project onto plane */
2934                                                 dist = -DotProduct( myTangent, myNormal );
2935                                                 VectorMA( myTangent, dist, myNormal, myTangent );
2936                                                 dist = -DotProduct( myBinormal, myNormal );
2937                                                 VectorMA( myBinormal, dist, myNormal, myBinormal );
2938
2939                                                 /* renormalize */
2940                                                 VectorNormalize( myTangent, myTangent );
2941                                                 VectorNormalize( myBinormal, myBinormal );
2942
2943                                                 /* convert modelspace deluxel to tangentspace */
2944                                                 dirSample[0] = bspDeluxel[0];
2945                                                 dirSample[1] = bspDeluxel[1];
2946                                                 dirSample[2] = bspDeluxel[2];
2947                                                 VectorNormalize( dirSample, dirSample );
2948
2949                                                 /* fix tangents to world matrix */
2950                                                 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2951                                                         VectorNegate( myTangent, myTangent );
2952                                                 }
2953
2954                                                 /* build tangentspace vectors */
2955                                                 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2956                                                 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2957                                                 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2958                                         }
2959                                 }
2960                         }
2961
2962                         Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
2963                 }
2964         }
2965
2966         /* -----------------------------------------------------------------
2967            blend lightmaps
2968            ----------------------------------------------------------------- */
2969
2970 #ifdef sdfsdfwq312323
2971         /* note it */
2972         Sys_Printf( "blending..." );
2973
2974         for ( i = 0; i < numRawLightmaps; i++ )
2975         {
2976                 vec3_t myColor;
2977                 float myBrightness;
2978
2979                 /* get lightmap */
2980                 lm = &rawLightmaps[ i ];
2981
2982                 /* walk individual lightmaps */
2983                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2984                 {
2985                         /* early outs */
2986                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2987                                 continue;
2988                         }
2989
2990                         /* walk lightmap samples */
2991                         for ( y = 0; y < lm->sh; y++ )
2992                         {
2993                                 for ( x = 0; x < lm->sw; x++ )
2994                                 {
2995                                         /* get luxel */
2996                                         bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2997
2998                                         /* get color */
2999                                         VectorNormalize( bspLuxel, myColor );
3000                                         myBrightness = VectorLength( bspLuxel );
3001                                         myBrightness *= ( 1 / 127.0f );
3002                                         myBrightness = myBrightness * myBrightness;
3003                                         myBrightness *= 127.0f;
3004                                         VectorScale( myColor, myBrightness, bspLuxel );
3005                                 }
3006                         }
3007                 }
3008         }
3009 #endif
3010
3011         /* -----------------------------------------------------------------
3012            collapse non-unique lightmaps
3013            ----------------------------------------------------------------- */
3014
3015         if ( noCollapse == qfalse && deluxemap == qfalse ) {
3016                 /* note it */
3017                 Sys_FPrintf( SYS_VRB, "collapsing..." );
3018
3019                 timer_start = I_FloatTime();
3020
3021                 /* set all twin refs to null */
3022                 for ( i = 0; i < numRawLightmaps; i++ )
3023                 {
3024                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3025                         {
3026                                 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
3027                                 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
3028                                 rawLightmaps[ i ].numStyledTwins = 0;
3029                         }
3030                 }
3031
3032                 /* walk the list of raw lightmaps */
3033                 for ( i = 0; i < numRawLightmaps; i++ )
3034                 {
3035                         /* get lightmap */
3036                         lm = &rawLightmaps[ i ];
3037
3038                         /* walk lightmaps */
3039                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3040                         {
3041                                 /* early outs */
3042                                 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
3043                                          lm->twins[ lightmapNum ] != NULL ) {
3044                                         continue;
3045                                 }
3046
3047                                 /* find all lightmaps that are virtually identical to this one */
3048                                 for ( j = i + 1; j < numRawLightmaps; j++ )
3049                                 {
3050                                         /* get lightmap */
3051                                         lm2 = &rawLightmaps[ j ];
3052
3053                                         /* walk lightmaps */
3054                                         for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3055                                         {
3056                                                 /* early outs */
3057                                                 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3058                                                          lm2->twins[ lightmapNum2 ] != NULL ) {
3059                                                         continue;
3060                                                 }
3061
3062                                                 /* compare them */
3063                                                 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3064                                                         /* merge and set twin */
3065                                                         if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3066                                                                 lm2->twins[ lightmapNum2 ] = lm;
3067                                                                 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3068                                                                 numTwins++;
3069                                                                 numTwinLuxels += ( lm->w * lm->h );
3070
3071                                                                 /* count styled twins */
3072                                                                 if ( lightmapNum > 0 ) {
3073                                                                         lm->numStyledTwins++;
3074                                                                 }
3075                                                         }
3076                                                 }
3077                                         }
3078                                 }
3079                         }
3080                 }
3081
3082                 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3083         }
3084
3085         /* -----------------------------------------------------------------
3086            sort raw lightmaps by shader
3087            ----------------------------------------------------------------- */
3088
3089         /* note it */
3090         Sys_FPrintf( SYS_VRB, "sorting..." );
3091
3092         timer_start = I_FloatTime();
3093
3094         /* allocate a new sorted list */
3095         if ( sortLightmaps == NULL ) {
3096                 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3097         }
3098
3099         /* fill it out and sort it */
3100         for ( i = 0; i < numRawLightmaps; i++ )
3101                 sortLightmaps[ i ] = i;
3102         qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3103
3104         Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3105
3106         /* -----------------------------------------------------------------
3107            allocate output lightmaps
3108            ----------------------------------------------------------------- */
3109
3110         /* note it */
3111         Sys_FPrintf( SYS_VRB, "allocating..." );
3112
3113         timer_start = I_FloatTime();
3114
3115         /* kill all existing output lightmaps */
3116         if ( outLightmaps != NULL ) {
3117                 for ( i = 0; i < numOutLightmaps; i++ )
3118                 {
3119                         free( outLightmaps[ i ].lightBits );
3120                         free( outLightmaps[ i ].bspLightBytes );
3121                 }
3122                 free( outLightmaps );
3123                 outLightmaps = NULL;
3124         }
3125
3126         numLightmapShaders = 0;
3127         numOutLightmaps = 0;
3128         numBSPLightmaps = 0;
3129         numExtLightmaps = 0;
3130
3131         /* find output lightmap */
3132         for ( i = 0; i < numRawLightmaps; i++ )
3133         {
3134                 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3135                 FindOutLightmaps( lm, fastAllocate );
3136         }
3137
3138         /* set output numbers in twinned lightmaps */
3139         for ( i = 0; i < numRawLightmaps; i++ )
3140         {
3141                 /* get lightmap */
3142                 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3143
3144                 /* walk lightmaps */
3145                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3146                 {
3147                         /* get twin */
3148                         lm2 = lm->twins[ lightmapNum ];
3149                         if ( lm2 == NULL ) {
3150                                 continue;
3151                         }
3152                         lightmapNum2 = lm->twinNums[ lightmapNum ];
3153
3154                         /* find output lightmap from twin */
3155                         lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3156                         lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3157                         lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3158                 }
3159         }
3160
3161         Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3162
3163         /* -----------------------------------------------------------------
3164            store output lightmaps
3165            ----------------------------------------------------------------- */
3166
3167         /* note it */
3168         Sys_FPrintf( SYS_VRB, "storing..." );
3169
3170         timer_start = I_FloatTime();
3171
3172         /* count the bsp lightmaps and allocate space */
3173         if ( bspLightBytes != NULL ) {
3174                 free( bspLightBytes );
3175         }
3176         if ( numBSPLightmaps == 0 || externalLightmaps ) {
3177                 numBSPLightBytes = 0;
3178                 bspLightBytes = NULL;
3179         }
3180         else
3181         {
3182                 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3183                 bspLightBytes = safe_malloc( numBSPLightBytes );
3184                 memset( bspLightBytes, 0, numBSPLightBytes );
3185         }
3186
3187         /* walk the list of output lightmaps */
3188         for ( i = 0; i < numOutLightmaps; i++ )
3189         {
3190                 /* get output lightmap */
3191                 olm = &outLightmaps[ i ];
3192
3193                 /* fill output lightmap */
3194                 if ( lightmapFill ) {
3195                         FillOutLightmap( olm );
3196                 }
3197
3198                 /* is this a valid bsp lightmap? */
3199                 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3200                         /* copy lighting data */
3201                         lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3202                         memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3203
3204                         /* copy direction data */
3205                         if ( deluxemap ) {
3206                                 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3207                                 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3208                         }
3209                 }
3210
3211                 /* external lightmap? */
3212                 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3213                         /* make a directory for the lightmaps */
3214                         Q_mkdir( dirname );
3215
3216                         /* set external lightmap number */
3217                         olm->extLightmapNum = numExtLightmaps;
3218
3219                         /* write lightmap */
3220                         sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3221                         Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3222                         WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3223                         numExtLightmaps++;
3224
3225                         /* write deluxemap */
3226                         if ( deluxemap ) {
3227                                 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3228                                 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3229                                 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3230                                 numExtLightmaps++;
3231
3232                                 if ( debugDeluxemap ) {
3233                                         olm->extLightmapNum++;
3234                                 }
3235                         }
3236                 }
3237         }
3238
3239         if ( numExtLightmaps > 0 ) {
3240                 Sys_FPrintf( SYS_VRB, "\n" );
3241         }
3242
3243         /* delete unused external lightmaps */
3244         for ( i = numExtLightmaps; i; i++ )
3245         {
3246                 /* determine if file exists */
3247                 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3248                 if ( !FileExists( filename ) ) {
3249                         break;
3250                 }
3251
3252                 /* delete it */
3253                 remove( filename );
3254         }
3255
3256         Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3257
3258         /* -----------------------------------------------------------------
3259            project the lightmaps onto the bsp surfaces
3260            ----------------------------------------------------------------- */
3261
3262         /* note it */
3263         Sys_FPrintf( SYS_VRB, "projecting..." );
3264
3265         timer_start = I_FloatTime();
3266
3267         /* walk the list of surfaces */
3268         for ( i = 0; i < numBSPDrawSurfaces; i++ )
3269         {
3270                 /* get the surface and info */
3271                 ds = &bspDrawSurfaces[ i ];
3272                 info = &surfaceInfos[ i ];
3273                 lm = info->lm;
3274                 olm = NULL;
3275
3276                 /* handle surfaces with identical parent */
3277                 if ( info->parentSurfaceNum >= 0 ) {
3278                         /* preserve original data and get parent */
3279                         parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3280                         memcpy( &dsTemp, ds, sizeof( *ds ) );
3281
3282                         /* overwrite child with parent data */
3283                         memcpy( ds, parent, sizeof( *ds ) );
3284
3285                         /* restore key parts */
3286                         ds->fogNum = dsTemp.fogNum;
3287                         ds->firstVert = dsTemp.firstVert;
3288                         ds->firstIndex = dsTemp.firstIndex;
3289                         memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3290
3291                         /* set vertex data */
3292                         dv = &bspDrawVerts[ ds->firstVert ];
3293                         dvParent = &bspDrawVerts[ parent->firstVert ];
3294                         for ( j = 0; j < ds->numVerts; j++ )
3295                         {
3296                                 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3297                                 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3298                         }
3299
3300                         /* skip the rest */
3301                         continue;
3302                 }
3303
3304                 /* handle vertex lit or approximated surfaces */
3305                 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3306                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3307                         {
3308                                 ds->lightmapNum[ lightmapNum ] = -3;
3309                                 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3310                         }
3311                 }
3312
3313                 /* handle lightmapped surfaces */
3314                 else
3315                 {
3316                         /* walk lightmaps */
3317                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3318                         {
3319                                 /* set style */
3320                                 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3321
3322                                 /* handle unused style */
3323                                 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3324                                         ds->lightmapNum[ lightmapNum ] = -3;
3325                                         continue;
3326                                 }
3327
3328                                 /* get output lightmap */
3329                                 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3330
3331                                 /* set bsp lightmap number */
3332                                 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3333
3334                                 /* deluxemap debugging makes the deluxemap visible */
3335                                 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3336                                         ds->lightmapNum[ lightmapNum ]++;
3337                                 }
3338
3339                                 /* calc lightmap origin in texture space */
3340                                 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3341                                 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3342
3343                                 /* calc lightmap st coords */
3344                                 dv = &bspDrawVerts[ ds->firstVert ];
3345                                 ydv = &yDrawVerts[ ds->firstVert ];
3346                                 for ( j = 0; j < ds->numVerts; j++ )
3347                                 {
3348                                         if ( lm->solid[ lightmapNum ] ) {
3349                                                 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3350                                                 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3351                                         }
3352                                         else
3353                                         {
3354                                                 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3355                                                 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3356                                         }
3357                                 }
3358                         }
3359                 }
3360
3361                 /* store vertex colors */
3362                 dv = &bspDrawVerts[ ds->firstVert ];
3363                 for ( j = 0; j < ds->numVerts; j++ )
3364                 {
3365                         /* walk lightmaps */
3366                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3367                         {
3368                                 /* handle unused style */
3369                                 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3370                                         VectorClear( color );
3371                                 }
3372                                 else
3373                                 {
3374                                         /* get vertex color */
3375                                         luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3376                                         VectorCopy( luxel, color );
3377
3378                                         /* set minimum light */
3379                                         if ( lightmapNum == 0 ) {
3380                                                 for ( k = 0; k < 3; k++ )
3381                                                         if ( color[ k ] < minVertexLight[ k ] ) {
3382                                                                 color[ k ] = minVertexLight[ k ];
3383                                                         }
3384                                         }
3385                                 }
3386
3387                                 /* store to bytes */
3388                                 if ( !info->si->noVertexLight ) {
3389                                         ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3390                                 }
3391                         }
3392                 }
3393
3394                 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3395                 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //%      info->si->styleMarker > 0 )
3396                         qboolean dfEqual;
3397                         char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3398
3399
3400                         /* setup */
3401                         sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3402                         dv = &bspDrawVerts[ ds->firstVert ];
3403
3404                         /* depthFunc equal? */
3405                         if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3406                                 dfEqual = qtrue;
3407                         }
3408                         else{
3409                                 dfEqual = qfalse;
3410                         }
3411
3412                         /* generate stages for styled lightmaps */
3413                         for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3414                         {
3415                                 /* early out */
3416                                 style = lm->styles[ lightmapNum ];
3417                                 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3418                                         continue;
3419                                 }
3420
3421                                 /* get output lightmap */
3422                                 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3423
3424                                 /* lightmap name */
3425                                 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3426                                         strcpy( lightmapName, "$lightmap" );
3427                                 }
3428                                 else{
3429                                         sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3430                                 }
3431
3432                                 /* get rgbgen string */
3433                                 if ( rgbGenValues[ style ] == NULL ) {
3434                                         sprintf( key, "_style%drgbgen", style );
3435                                         rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3436                                         if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3437                                                 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3438                                         }
3439                                 }
3440                                 rgbGen[ 0 ] = '\0';
3441                                 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3442                                         sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3443                                 }
3444                                 else{
3445                                         rgbGen[ 0 ] = '\0';
3446                                 }
3447
3448                                 /* get alphagen string */
3449                                 if ( alphaGenValues[ style ] == NULL ) {
3450                                         sprintf( key, "_style%dalphagen", style );
3451                                         alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3452                                 }
3453                                 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3454                                         sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3455                                 }
3456                                 else{
3457                                         alphaGen[ 0 ] = '\0';
3458                                 }
3459
3460                                 /* calculate st offset */
3461                                 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3462                                 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3463
3464                                 /* create additional stage */
3465                                 if ( lmx == 0.0f && lmy == 0.0f ) {
3466                                         sprintf( styleStage,    "\t{\n"
3467                                                                                         "\t\tmap %s\n"                                      /* lightmap */
3468                                                                                         "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3469                                                                                         "%s"                                                /* depthFunc equal */
3470                                                                                         "%s"                                                /* rgbGen */
3471                                                                                         "%s"                                                /* alphaGen */
3472                                                                                         "\t\ttcGen lightmap\n"
3473                                                                                         "\t}\n",
3474                                                          lightmapName,
3475                                                          ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3476                                                          rgbGen,
3477                                                          alphaGen );
3478                                 }
3479                                 else
3480                                 {
3481                                         sprintf( styleStage,    "\t{\n"
3482                                                                                         "\t\tmap %s\n"                                      /* lightmap */
3483                                                                                         "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3484                                                                                         "%s"                                                /* depthFunc equal */
3485                                                                                         "%s"                                                /* rgbGen */
3486                                                                                         "%s"                                                /* alphaGen */
3487                                                                                         "\t\ttcGen lightmap\n"
3488                                                                                         "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n"         /* st offset */
3489                                                                                         "\t}\n",
3490                                                          lightmapName,
3491                                                          ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3492                                                          rgbGen,
3493                                                          alphaGen,
3494                                                          lmx, lmy );
3495
3496                                 }
3497
3498                                 /* concatenate */
3499                                 strcat( styleStages, styleStage );
3500                         }
3501
3502                         /* create custom shader */
3503                         if ( info->si->styleMarker == 2 ) {
3504                                 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3505                         }
3506                         else{
3507                                 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3508                         }
3509
3510                         /* emit remap command */
3511                         //%     EmitVertexRemapShader( csi->shader, info->si->shader );
3512
3513                         /* store it */
3514                         //%     Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3515                         ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3516                         //%     Sys_Printf( ")\n" );
3517                 }
3518
3519                 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3520                 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3521                                   ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3522                         /* get output lightmap */
3523                         olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3524
3525                         /* do some name mangling */
3526                         sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3527
3528                         /* create custom shader */
3529                         csi = CustomShader( info->si, "$lightmap", lightmapName );
3530
3531                         /* store it */
3532                         //%     Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3533                         ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3534                         //%     Sys_Printf( ")\n" );
3535                 }
3536
3537                 /* use the normal plain-jane shader */
3538                 else{
3539                         ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3540                 }
3541         }
3542
3543         Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3544
3545         /* finish */
3546         Sys_FPrintf( SYS_VRB, "done.\n" );
3547
3548         /* calc num stored */
3549         numStored = numBSPLightBytes / 3;
3550         efficiency = ( numStored <= 0 )
3551                                  ? 0
3552                                  : (float) numUsed / (float) numStored;
3553
3554         /* print stats */
3555         Sys_Printf( "%9d luxels used\n", numUsed );
3556         Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3557         Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3558         Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3559         Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3560         Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3561         Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3562         Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3563         Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3564
3565         /* write map shader file */
3566         WriteMapShaderFile();
3567 }