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