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