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