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