1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
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.
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.
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
22 ----------------------------------------------------------------------------------
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."
27 ------------------------------------------------------------------------------- */
32 #define LIGHTMAPS_YDNAR_C
42 /* -------------------------------------------------------------------------------
44 this file contains code that doe lightmap allocation and projection that
45 runs in the -light phase.
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.
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.
55 ------------------------------------------------------------------------------- */
59 based on WriteTGA() from imagelib.c
62 void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip )
69 /* allocate a buffer and set it up */
70 buffer = safe_malloc( width * height * 3 + 18 );
71 memset( buffer, 0, 18 );
73 buffer[ 12 ] = width & 255;
74 buffer[ 13 ] = width >> 8;
75 buffer[ 14 ] = height & 255;
76 buffer[ 15 ] = height >> 8;
80 c = (width * height * 3) + 18;
81 for( i = 18; i < c; i += 3 )
83 buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
84 buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
85 buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
88 /* write it and free the buffer */
89 file = fopen( filename, "wb" );
91 Error( "Unable to open %s for writing", filename );
93 /* flip vertically? */
96 fwrite( buffer, 1, 18, file );
97 for( in = buffer + ((height - 1) * width * 3) + 18; in >= buffer; in -= (width * 3) )
98 fwrite( in, 1, (width * 3), file );
101 fwrite( buffer, 1, c, file );
112 exports the lightmaps as a list of numbered tga images
115 void ExportLightmaps( void )
118 char dirname[ 1024 ], filename[ 1024 ];
123 Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n");
125 /* do some path mangling */
126 strcpy( dirname, source );
127 StripExtension( dirname );
130 if( bspLightBytes == NULL )
132 Sys_Printf( "WARNING: No BSP lightmap data\n" );
136 /* make a directory for the lightmaps */
139 /* iterate through the lightmaps */
140 for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (game->lightmapSize * game->lightmapSize * 3) )
142 /* write a tga image out */
143 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
144 Sys_Printf( "Writing %s\n", filename );
145 WriteTGA24( filename, lightmap, game->lightmapSize, game->lightmapSize, qfalse );
152 ExportLightmapsMain()
153 exports the lightmaps as a list of numbered tga images
156 int ExportLightmapsMain( int argc, char **argv )
161 Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
165 /* do some path mangling */
166 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
167 StripExtension( source );
168 DefaultExtension( source, ".bsp" );
171 Sys_Printf( "Loading %s\n", source );
172 LoadBSPFile( source );
174 /* export the lightmaps */
177 /* return to sender */
184 ImportLightmapsMain()
185 imports the lightmaps from a list of numbered tga images
188 int ImportLightmapsMain( int argc, char **argv )
190 int i, x, y, len, width, height;
191 char dirname[ 1024 ], filename[ 1024 ];
192 byte *lightmap, *buffer, *pixels, *in, *out;
198 Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
202 /* do some path mangling */
203 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
204 StripExtension( source );
205 DefaultExtension( source, ".bsp" );
208 Sys_Printf( "Loading %s\n", source );
209 LoadBSPFile( source );
212 Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n");
214 /* do some path mangling */
215 strcpy( dirname, source );
216 StripExtension( dirname );
219 if( bspLightBytes == NULL )
220 Error( "No lightmap data" );
222 /* make a directory for the lightmaps */
225 /* iterate through the lightmaps */
226 for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (game->lightmapSize * game->lightmapSize * 3) )
228 /* read a tga image */
229 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
230 Sys_Printf( "Loading %s\n", filename );
232 len = vfsLoadFile( filename, (void*) &buffer, -1 );
235 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
239 /* parse file into an image */
241 LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
244 /* sanity check it */
247 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
250 if( width != game->lightmapSize || height != game->lightmapSize )
251 Sys_Printf( "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
252 filename, width, height, game->lightmapSize, game->lightmapSize );
254 /* copy the pixels */
256 for( y = 1; y <= game->lightmapSize; y++ )
258 out = lightmap + ((game->lightmapSize - y) * game->lightmapSize * 3);
259 for( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
260 VectorCopy( in, out );
268 Sys_Printf( "writing %s\n", source );
269 WriteBSPFile( source );
271 /* return to sender */
277 /* -------------------------------------------------------------------------------
279 this section deals with projecting a lightmap onto a raw drawsurface
281 ------------------------------------------------------------------------------- */
284 CompareLightSurface()
285 compare function for qsort()
288 static int CompareLightSurface( const void *a, const void *b )
290 shaderInfo_t *asi, *bsi;
294 asi = surfaceInfos[ *((int*) a) ].si;
295 bsi = surfaceInfos[ *((int*) b) ].si;
303 /* compare shader names */
304 return strcmp( asi->shader, bsi->shader );
311 allocates a raw lightmap's necessary buffers
314 void FinishRawLightmap( rawLightmap_t *lm )
316 int i, j, c, size, *sc;
321 /* sort light surfaces by shader name */
322 qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
325 lm->numLightClusters = 0;
326 for( i = 0; i < lm->numLightSurfaces; i++ )
328 /* get surface info */
329 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
331 /* add surface clusters */
332 lm->numLightClusters += info->numSurfaceClusters;
335 /* allocate buffer for clusters and copy */
336 lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
338 for( i = 0; i < lm->numLightSurfaces; i++ )
340 /* get surface info */
341 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
343 /* add surface clusters */
344 for( j = 0; j < info->numSurfaceClusters; j++ )
345 lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
349 lm->styles[ 0 ] = LS_NORMAL;
350 for( i = 1; i < MAX_LIGHTMAPS; i++ )
351 lm->styles[ i ] = LS_NONE;
353 /* set supersampling size */
354 lm->sw = lm->w * superSample;
355 lm->sh = lm->h * superSample;
357 /* add to super luxel count */
358 numRawSuperLuxels += (lm->sw * lm->sh);
360 /* manipulate origin/vecs for supersampling */
361 if( superSample > 1 && lm->vecs != NULL )
363 /* calc inverse supersample */
364 is = 1.0f / superSample;
366 /* scale the vectors and shift the origin */
368 /* new code that works for arbitrary supersampling values */
369 VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
370 VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
371 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
372 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
373 VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
374 VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
376 /* old code that only worked with a value of 2 */
377 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
378 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
379 VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
380 VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
384 /* allocate bsp lightmap storage */
385 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
386 if( lm->bspLuxels[ 0 ] == NULL )
387 lm->bspLuxels[ 0 ] = safe_malloc( size );
388 memset( lm->bspLuxels[ 0 ], 0, size );
390 /* allocate radiosity lightmap storage */
393 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
394 if( lm->radLuxels[ 0 ] == NULL )
395 lm->radLuxels[ 0 ] = safe_malloc( size );
396 memset( lm->radLuxels[ 0 ], 0, size );
399 /* allocate sampling lightmap storage */
400 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
401 if( lm->superLuxels[ 0 ] == NULL )
402 lm->superLuxels[ 0 ] = safe_malloc( size );
403 memset( lm->superLuxels[ 0 ], 0, size );
405 /* allocate origin map storage */
406 size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
407 if( lm->superOrigins == NULL )
408 lm->superOrigins = safe_malloc( size );
409 memset( lm->superOrigins, 0, size );
411 /* allocate normal map storage */
412 size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
413 if( lm->superNormals == NULL )
414 lm->superNormals = safe_malloc( size );
415 memset( lm->superNormals, 0, size );
417 /* allocate floodlight map storage */
418 size = lm->sw * lm->sh * SUPER_FLOODLIGHT_SIZE * sizeof( float );
419 if( lm->superFloodLight == NULL )
420 lm->superFloodLight = safe_malloc( size );
421 memset( lm->superFloodLight, 0, size );
423 /* allocate cluster map storage */
424 size = lm->sw * lm->sh * sizeof( int );
425 if( lm->superClusters == NULL )
426 lm->superClusters = safe_malloc( size );
427 size = lm->sw * lm->sh;
428 sc = lm->superClusters;
429 for( i = 0; i < size; i++ )
430 (*sc++) = CLUSTER_UNMAPPED;
432 /* deluxemap allocation */
435 /* allocate sampling deluxel storage */
436 size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
437 if( lm->superDeluxels == NULL )
438 lm->superDeluxels = safe_malloc( size );
439 memset( lm->superDeluxels, 0, size );
441 /* allocate bsp deluxel storage */
442 size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
443 if( lm->bspDeluxels == NULL )
444 lm->bspDeluxels = safe_malloc( size );
445 memset( lm->bspDeluxels, 0, size );
449 numLuxels += (lm->sw * lm->sh);
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()
462 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm )
464 bspDrawSurface_t *ds;
467 bspDrawVert_t *verts, *a, *b;
469 mesh_t src, *subdivided, *mesh;
470 float sBasis, tBasis, s, t;
471 float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
474 /* patches finish a raw lightmap */
475 lm->finished = qtrue;
477 /* get surface and info */
478 ds = &bspDrawSurfaces[ num ];
479 info = &surfaceInfos[ num ];
481 /* make a temporary mesh from the drawsurf */
482 src.width = ds->patchWidth;
483 src.height = ds->patchHeight;
484 src.verts = &yDrawVerts[ ds->firstVert ];
485 //% subdivided = SubdivideMesh( src, 8, 512 );
486 subdivided = SubdivideMesh2( src, info->patchIterations );
488 /* fit it to the curve and remove colinear verts on rows/columns */
489 PutMeshOnCurve( *subdivided );
490 mesh = RemoveLinearMeshColumnsRows( subdivided );
491 FreeMesh( subdivided );
493 /* find the longest distance on each row/column */
495 memset( widthTable, 0, sizeof( widthTable ) );
496 memset( heightTable, 0, sizeof( heightTable ) );
497 for( y = 0; y < mesh->height; y++ )
499 for( x = 0; x < mesh->width; x++ )
502 if( x + 1 < mesh->width )
504 a = &verts[ (y * mesh->width) + x ];
505 b = &verts[ (y * mesh->width) + x + 1 ];
506 VectorSubtract( a->xyz, b->xyz, delta );
507 length = VectorLength( delta );
508 if( length > widthTable[ x ] )
509 widthTable[ x ] = length;
513 if( y + 1 < mesh->height )
515 a = &verts[ (y * mesh->width) + x ];
516 b = &verts[ ((y + 1) * mesh->width) + x ];
517 VectorSubtract( a->xyz, b->xyz, delta );
518 length = VectorLength( delta );
519 if( length > heightTable[ y ] )
520 heightTable[ y ] = length;
525 /* determine lightmap width */
527 for( x = 0; x < (mesh->width - 1); x++ )
528 length += widthTable[ x ];
529 lm->w = ceil( length / lm->sampleSize ) + 1;
530 if( lm->w < ds->patchWidth )
531 lm->w = ds->patchWidth;
532 if( lm->w > lm->customWidth )
533 lm->w = lm->customWidth;
534 sBasis = (float) (lm->w - 1) / (float) (ds->patchWidth - 1);
536 /* determine lightmap height */
538 for( y = 0; y < (mesh->height - 1); y++ )
539 length += heightTable[ y ];
540 lm->h = ceil( length / lm->sampleSize ) + 1;
541 if( lm->h < ds->patchHeight )
542 lm->h = ds->patchHeight;
543 if( lm->h > lm->customHeight )
544 lm->h = lm->customHeight;
545 tBasis = (float) (lm->h - 1) / (float) (ds->patchHeight - 1);
547 /* free the temporary mesh */
550 /* set the lightmap texture coordinates in yDrawVerts */
551 lm->wrap[ 0 ] = qtrue;
552 lm->wrap[ 1 ] = qtrue;
553 verts = &yDrawVerts[ ds->firstVert ];
554 for( y = 0; y < ds->patchHeight; y++ )
556 t = (tBasis * y) + 0.5f;
557 for( x = 0; x < ds->patchWidth; x++ )
559 s = (sBasis * x) + 0.5f;
560 verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
561 verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
563 if( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ((ds->patchHeight - 1) * ds->patchWidth) + x ].xyz ) )
564 lm->wrap[ 1 ] = qfalse;
567 if( !VectorCompare( verts[ (y * ds->patchWidth) ].xyz, verts[ (y * ds->patchWidth) + (ds->patchWidth - 1) ].xyz ) )
568 lm->wrap[ 0 ] = qfalse;
572 //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
573 //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
574 //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
575 //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
576 //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
579 numPatchesLightmapped++;
588 AddSurfaceToRawLightmap()
589 projects a lightmap for a surface
590 based on AllocateLightmapForSurface()
593 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm )
595 bspDrawSurface_t *ds, *ds2;
596 surfaceInfo_t *info, *info2;
597 int num2, n, i, axisNum;
598 float s, t, d, len, sampleSize;
599 vec3_t mins, maxs, origin, faxis, size, exactSize, delta, normalized, vecs[ 2 ];
601 bspDrawVert_t *verts;
604 /* get surface and info */
605 ds = &bspDrawSurfaces[ num ];
606 info = &surfaceInfos[ num ];
608 /* add the surface to the raw lightmap */
609 lightSurfaces[ numLightSurfaces++ ] = num;
610 lm->numLightSurfaces++;
612 /* does this raw lightmap already have any surfaces? */
613 if( lm->numLightSurfaces > 1 )
615 /* surface and raw lightmap must have the same lightmap projection axis */
616 if( VectorCompare( info->axis, lm->axis ) == qfalse )
619 /* match identical attributes */
620 if( info->sampleSize != lm->sampleSize ||
621 info->entityNum != lm->entityNum ||
622 info->recvShadows != lm->recvShadows ||
623 info->si->lmCustomWidth != lm->customWidth ||
624 info->si->lmCustomHeight != lm->customHeight ||
625 info->si->lmBrightness != lm->brightness ||
626 info->si->lmFilterRadius != lm->filterRadius ||
627 info->si->splotchFix != lm->splotchFix )
630 /* surface bounds must intersect with raw lightmap bounds */
631 for( i = 0; i < 3; i++ )
633 if( info->mins[ i ] > lm->maxs[ i ] )
635 if( info->maxs[ i ] < lm->mins[ i ] )
639 /* plane check (fixme: allow merging of nonplanars) */
640 if( info->si->lmMergable == qfalse )
642 if( info->plane == NULL || lm->plane == NULL )
646 for( i = 0; i < 4; i++ )
647 if( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON )
651 /* debug code hacking */
652 //% if( lm->numLightSurfaces > 1 )
657 if( info->plane == NULL )
660 /* add surface to lightmap bounds */
661 AddPointToBounds( info->mins, lm->mins, lm->maxs );
662 AddPointToBounds( info->maxs, lm->mins, lm->maxs );
664 /* check to see if this is a non-planar patch */
665 if( ds->surfaceType == MST_PATCH &&
666 lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f )
667 return AddPatchToRawLightmap( num, lm );
669 /* start with initially requested sample size */
670 sampleSize = lm->sampleSize;
672 /* round to the lightmap resolution */
673 for( i = 0; i < 3; i++ )
675 exactSize[ i ] = lm->maxs[ i ] - lm->mins[ i ];
676 mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
677 maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
678 size[ i ] = (maxs[ i ] - mins[ i ]) / sampleSize + 1.0f;
680 /* hack (god this sucks) */
681 if( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight )
688 /* set actual sample size */
689 lm->actualSampleSize = sampleSize;
691 /* fixme: copy rounded mins/maxes to lightmap record? */
692 if( lm->plane == NULL )
694 VectorCopy( mins, lm->mins );
695 VectorCopy( maxs, lm->maxs );
696 VectorCopy( mins, origin );
699 /* set lightmap origin */
700 VectorCopy( lm->mins, origin );
702 /* make absolute axis */
703 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
704 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
705 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
707 /* clear out lightmap vectors */
708 memset( vecs, 0, sizeof( vecs ) );
710 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
711 if( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] )
716 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
717 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
719 else if( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] )
724 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
725 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
732 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
733 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
736 /* check for bogus axis */
737 if( faxis[ axisNum ] == 0.0f )
739 Sys_Printf( "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
744 /* store the axis number in the lightmap */
745 lm->axisNum = axisNum;
747 /* walk the list of surfaces on this raw lightmap */
748 for( n = 0; n < lm->numLightSurfaces; n++ )
751 num2 = lightSurfaces[ lm->firstLightSurface + n ];
752 ds2 = &bspDrawSurfaces[ num2 ];
753 info2 = &surfaceInfos[ num2 ];
754 verts = &yDrawVerts[ ds2->firstVert ];
756 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
757 for( i = 0; i < ds2->numVerts; i++ )
759 VectorSubtract( verts[ i ].xyz, origin, delta );
760 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
761 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
762 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
763 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
765 if( s > (float) lm->w || t > (float) lm->h )
767 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
768 s, lm->w, t, lm->h );
773 /* get first drawsurface */
774 num2 = lightSurfaces[ lm->firstLightSurface ];
775 ds2 = &bspDrawSurfaces[ num2 ];
776 info2 = &surfaceInfos[ num2 ];
777 verts = &yDrawVerts[ ds2->firstVert ];
779 /* calculate lightmap origin */
780 if( VectorLength( ds2->lightmapVecs[ 2 ] ) )
781 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
783 VectorCopy( lm->axis, plane );
784 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
786 VectorCopy( origin, lm->origin );
787 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
788 d /= plane[ axisNum ];
789 lm->origin[ axisNum ] -= d;
792 VectorCopy( lm->origin, ds->lightmapOrigin );
794 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
795 if( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) /* ydnar: can't remember what exactly i was thinking here... */
797 /* allocate space for the vectors */
798 lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
799 memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
800 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
802 /* project stepped lightmap blocks and subtract to get planevecs */
803 for( i = 0; i < 2; i++ )
805 len = VectorNormalize( vecs[ i ], normalized );
806 VectorScale( normalized, (1.0 / len), lm->vecs[ i ] );
807 d = DotProduct( lm->vecs[ i ], plane );
808 d /= plane[ axisNum ];
809 lm->vecs[ i ][ axisNum ] -= d;
814 /* lightmap vectors are useless on a non-planar surface */
819 if( ds->surfaceType == MST_PATCH )
821 numPatchesLightmapped++;
822 if( lm->plane != NULL )
823 numPlanarPatchesLightmapped++;
827 if( lm->plane != NULL )
828 numPlanarsLightmapped++;
830 numNonPlanarsLightmapped++;
841 compare function for qsort()
844 static int CompareSurfaceInfo( const void *a, const void *b )
846 surfaceInfo_t *aInfo, *bInfo;
850 /* get surface info */
851 aInfo = &surfaceInfos[ *((int*) a) ];
852 bInfo = &surfaceInfos[ *((int*) b) ];
855 if( aInfo->model < bInfo->model )
857 else if( aInfo->model > bInfo->model )
860 /* then lightmap status */
861 if( aInfo->hasLightmap < bInfo->hasLightmap )
863 else if( aInfo->hasLightmap > bInfo->hasLightmap )
866 /* then lightmap sample size */
867 if( aInfo->sampleSize < bInfo->sampleSize )
869 else if( aInfo->sampleSize > bInfo->sampleSize )
872 /* then lightmap axis */
873 for( i = 0; i < 3; i++ )
875 if( aInfo->axis[ i ] < bInfo->axis[ i ] )
877 else if( aInfo->axis[ i ] > bInfo->axis[ i ] )
882 if( aInfo->plane == NULL && bInfo->plane != NULL )
884 else if( aInfo->plane != NULL && bInfo->plane == NULL )
886 else if( aInfo->plane != NULL && bInfo->plane != NULL )
888 for( i = 0; i < 4; i++ )
890 if( aInfo->plane[ i ] < bInfo->plane[ i ] )
892 else if( aInfo->plane[ i ] > bInfo->plane[ i ] )
897 /* then position in world */
898 for( i = 0; i < 3; i++ )
900 if( aInfo->mins[ i ] < bInfo->mins[ i ] )
902 else if( aInfo->mins[ i ] > bInfo->mins[ i ] )
906 /* these are functionally identical (this should almost never happen) */
913 SetupSurfaceLightmaps()
914 allocates lightmaps for every surface in the bsp that needs one
915 this depends on yDrawVerts being allocated
918 void SetupSurfaceLightmaps( void )
920 int i, j, k, s,num, num2;
923 bspDrawSurface_t *ds, *ds2;
924 surfaceInfo_t *info, *info2;
927 vec3_t mapSize, entityOrigin;
931 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n");
933 /* determine supersample amount */
934 if( superSample < 1 )
936 else if( superSample > 8 )
938 Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
942 /* clear map bounds */
943 ClearBounds( mapMins, mapMaxs );
945 /* allocate a list of surface clusters */
946 numSurfaceClusters = 0;
947 maxSurfaceClusters = numBSPLeafSurfaces;
948 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
949 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
951 /* allocate a list for per-surface info */
952 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
953 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
954 for( i = 0; i < numBSPDrawSurfaces; i++ )
955 surfaceInfos[ i ].childSurfaceNum = -1;
957 /* allocate a list of surface indexes to be sorted */
958 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
959 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
961 /* walk each model in the bsp */
962 for( i = 0; i < numBSPModels; i++ )
965 model = &bspModels[ i ];
967 /* walk the list of surfaces in this model and fill out the info structs */
968 for( j = 0; j < model->numBSPSurfaces; j++ )
970 /* make surface index */
971 num = model->firstBSPSurface + j;
973 /* copy index to sort list */
974 sortSurfaces[ num ] = num;
976 /* get surface and info */
977 ds = &bspDrawSurfaces[ num ];
978 info = &surfaceInfos[ num ];
980 /* set entity origin */
981 if( ds->numVerts > 0 )
982 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
984 VectorClear( entityOrigin );
990 info->firstSurfaceCluster = numSurfaceClusters;
993 info->si = GetSurfaceExtraShaderInfo( num );
994 if( info->si == NULL )
995 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
996 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
997 info->entityNum = GetSurfaceExtraEntityNum( num );
998 info->castShadows = GetSurfaceExtraCastShadows( num );
999 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1000 info->sampleSize = GetSurfaceExtraSampleSize( num );
1001 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1002 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1003 GetSurfaceExtraLightmapAxis( num, info->axis );
1006 if( info->parentSurfaceNum >= 0 )
1007 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1009 /* determine surface bounds */
1010 ClearBounds( info->mins, info->maxs );
1011 for( k = 0; k < ds->numVerts; k++ )
1013 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1014 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1017 /* find all the bsp clusters the surface falls into */
1018 for( k = 0; k < numBSPLeafs; k++ )
1021 leaf = &bspLeafs[ k ];
1024 if( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1025 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1026 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] )
1029 /* test leaf surfaces */
1030 for( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1032 if( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num )
1034 if( numSurfaceClusters >= maxSurfaceClusters )
1035 Error( "maxSurfaceClusters exceeded" );
1036 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1037 numSurfaceClusters++;
1038 info->numSurfaceClusters++;
1043 /* determine if surface is planar */
1044 if( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f )
1047 info->plane = safe_malloc( 4 * sizeof( float ) );
1048 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1049 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1052 /* determine if surface requires a lightmap */
1053 if( ds->surfaceType == MST_TRIANGLE_SOUP ||
1054 ds->surfaceType == MST_FOLIAGE ||
1055 (info->si->compileFlags & C_VERTEXLIT) )
1056 numSurfsVertexLit++;
1059 numSurfsLightmapped++;
1060 info->hasLightmap = qtrue;
1065 /* find longest map distance */
1066 VectorSubtract( mapMaxs, mapMins, mapSize );
1067 maxMapDistance = VectorLength( mapSize );
1069 /* sort the surfaces info list */
1070 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1072 /* allocate a list of surfaces that would go into raw lightmaps */
1073 numLightSurfaces = 0;
1074 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1075 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1077 /* allocate a list of raw lightmaps */
1078 numRawSuperLuxels = 0;
1079 numRawLightmaps = 0;
1080 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1081 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1083 /* walk the list of sorted surfaces */
1084 for( i = 0; i < numBSPDrawSurfaces; i++ )
1086 /* get info and attempt early out */
1087 num = sortSurfaces[ i ];
1088 ds = &bspDrawSurfaces[ num ];
1089 info = &surfaceInfos[ num ];
1090 if( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 )
1093 /* allocate a new raw lightmap */
1094 lm = &rawLightmaps[ numRawLightmaps ];
1098 lm->splotchFix = info->si->splotchFix;
1099 lm->firstLightSurface = numLightSurfaces;
1100 lm->numLightSurfaces = 0;
1101 lm->sampleSize = info->sampleSize;
1102 lm->actualSampleSize = info->sampleSize;
1103 lm->entityNum = info->entityNum;
1104 lm->recvShadows = info->recvShadows;
1105 lm->brightness = info->si->lmBrightness;
1106 lm->filterRadius = info->si->lmFilterRadius;
1107 VectorCopy( info->axis, lm->axis );
1108 lm->plane = info->plane;
1109 VectorCopy( info->mins, lm->mins );
1110 VectorCopy( info->maxs, lm->maxs );
1112 lm->customWidth = info->si->lmCustomWidth;
1113 lm->customHeight = info->si->lmCustomHeight;
1115 /* add the surface to the raw lightmap */
1116 AddSurfaceToRawLightmap( num, lm );
1119 /* do an exhaustive merge */
1123 /* walk the list of surfaces again */
1125 for( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1127 /* get info and attempt early out */
1128 num2 = sortSurfaces[ j ];
1129 ds2 = &bspDrawSurfaces[ num2 ];
1130 info2 = &surfaceInfos[ num2 ];
1131 if( info2->hasLightmap == qfalse || info2->lm != NULL )
1134 /* add the surface to the raw lightmap */
1135 if( AddSurfaceToRawLightmap( num2, lm ) )
1143 lm->numLightSurfaces--;
1149 /* finish the lightmap and allocate the various buffers */
1150 FinishRawLightmap( lm );
1153 /* allocate vertex luxel storage */
1154 for( k = 0; k < MAX_LIGHTMAPS; k++ )
1156 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1157 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1158 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1159 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1162 /* emit some stats */
1163 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1164 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1165 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1166 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1167 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1168 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1169 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1170 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1176 StitchSurfaceLightmaps()
1177 stitches lightmap edges
1178 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1181 #define MAX_STITCH_CANDIDATES 32
1182 #define MAX_STITCH_LUXELS 64
1184 void StitchSurfaceLightmaps( void )
1186 int i, j, x, y, x2, y2, *cluster, *cluster2,
1187 numStitched, numCandidates, numLuxels, f, fOld, start;
1188 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1189 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1190 sampleSize, average[ 3 ], totalColor, ootc, *luxels[ MAX_STITCH_LUXELS ];
1193 /* disabled for now */
1197 Sys_Printf( "--- StitchSurfaceLightmaps ---\n");
1201 start = I_FloatTime();
1203 /* walk the list of raw lightmaps */
1205 for( i = 0; i < numRawLightmaps; i++ )
1207 /* print pacifier */
1208 f = 10 * i / numRawLightmaps;
1212 Sys_Printf( "%i...", f );
1215 /* get lightmap a */
1216 a = &rawLightmaps[ i ];
1218 /* walk rest of lightmaps */
1220 for( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1222 /* get lightmap b */
1223 b = &rawLightmaps[ j ];
1225 /* test bounding box */
1226 if( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1227 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1228 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] )
1232 c[ numCandidates++ ] = b;
1236 for( y = 0; y < a->sh; y++ )
1238 for( x = 0; x < a->sw; x++ )
1240 /* ignore unmapped/unlit luxels */
1242 cluster = SUPER_CLUSTER( x, y );
1243 if( *cluster == CLUSTER_UNMAPPED )
1245 luxel = SUPER_LUXEL( 0, x, y );
1246 if( luxel[ 3 ] <= 0.0f )
1249 /* get particulars */
1250 origin = SUPER_ORIGIN( x, y );
1251 normal = SUPER_NORMAL( x, y );
1253 /* walk candidate list */
1254 for( j = 0; j < numCandidates; j++ )
1260 /* set samplesize to the smaller of the pair */
1261 sampleSize = 0.5f * (a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize);
1263 /* test bounding box */
1264 if( origin[ 0 ] < (b->mins[ 0 ] - sampleSize) || (origin[ 0 ] > b->maxs[ 0 ] + sampleSize) ||
1265 origin[ 1 ] < (b->mins[ 1 ] - sampleSize) || (origin[ 1 ] > b->maxs[ 1 ] + sampleSize) ||
1266 origin[ 2 ] < (b->mins[ 2 ] - sampleSize) || (origin[ 2 ] > b->maxs[ 2 ] + sampleSize) )
1269 /* walk candidate luxels */
1270 VectorClear( average );
1273 for( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1275 for( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1277 /* ignore same luxels */
1278 if( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 )
1281 /* ignore unmapped/unlit luxels */
1282 cluster2 = SUPER_CLUSTER( x2, y2 );
1283 if( *cluster2 == CLUSTER_UNMAPPED )
1285 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1286 if( luxel2[ 3 ] <= 0.0f )
1289 /* get particulars */
1290 origin2 = SUPER_ORIGIN( x2, y2 );
1291 normal2 = SUPER_NORMAL( x2, y2 );
1294 if( DotProduct( normal, normal2 ) < 0.5f )
1298 if( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1299 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1300 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize )
1304 //% VectorSet( luxel2, 255, 0, 255 );
1305 luxels[ numLuxels++ ] = luxel2;
1306 VectorAdd( average, luxel2, average );
1307 totalColor += luxel2[ 3 ];
1312 if( numLuxels == 0 )
1316 ootc = 1.0f / totalColor;
1317 VectorScale( average, ootc, luxel );
1325 /* emit statistics */
1326 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
1327 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1334 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1337 #define SOLID_EPSILON 0.0625
1338 #define LUXEL_TOLERANCE 0.0025
1339 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1341 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1345 double delta, total, rd, gd, bd;
1346 float *aLuxel, *bLuxel;
1349 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1350 if( (minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ]) &&
1351 ((aNum == 0 && bNum != 0) || (aNum != 0 && bNum == 0)) )
1355 if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1356 a->brightness != b->brightness ||
1357 a->solid[ aNum ] != b->solid[ bNum ] ||
1358 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1361 /* compare solid color lightmaps */
1362 if( a->solid[ aNum ] && b->solid[ bNum ] )
1365 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1366 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1367 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1370 if( rd > SOLID_EPSILON || gd > SOLID_EPSILON|| bd > SOLID_EPSILON )
1377 /* compare nonsolid lightmaps */
1378 if( a->w != b->w || a->h != b->h )
1381 /* compare luxels */
1384 for( y = 0; y < a->h; y++ )
1386 for( x = 0; x < a->w; x++ )
1388 /* increment total */
1392 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1393 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1395 /* ignore unused luxels */
1396 if( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 )
1400 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1401 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1402 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1404 /* 2003-09-27: compare individual luxels */
1405 if( rd > 3.0 || gd > 3.0 || bd > 3.0 )
1408 /* compare (fixme: take into account perceptual differences) */
1409 delta += rd * LUXEL_COLOR_FRAC;
1410 delta += gd * LUXEL_COLOR_FRAC;
1411 delta += bd * LUXEL_COLOR_FRAC;
1413 /* is the change too high? */
1414 if( total > 0.0 && ((delta / total) > LUXEL_TOLERANCE) )
1419 /* made it this far, they must be identical (or close enough) */
1427 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1430 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1434 float luxel[ 3 ], *aLuxel, *bLuxel;
1438 if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1439 a->brightness != b->brightness ||
1440 a->solid[ aNum ] != b->solid[ bNum ] ||
1441 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1444 /* compare solid lightmaps */
1445 if( a->solid[ aNum ] && b->solid[ bNum ] )
1448 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1449 VectorScale( luxel, 0.5f, luxel );
1452 VectorCopy( luxel, a->solidColor[ aNum ] );
1453 VectorCopy( luxel, b->solidColor[ bNum ] );
1455 /* return to sender */
1459 /* compare nonsolid lightmaps */
1460 if( a->w != b->w || a->h != b->h )
1464 for( y = 0; y < a->h; y++ )
1466 for( x = 0; x < a->w; x++ )
1469 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1470 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1472 /* handle occlusion mismatch */
1473 if( aLuxel[ 0 ] < 0.0f )
1474 VectorCopy( bLuxel, aLuxel );
1475 else if( bLuxel[ 0 ] < 0.0f )
1476 VectorCopy( aLuxel, bLuxel );
1480 VectorAdd( aLuxel, bLuxel, luxel );
1481 VectorScale( luxel, 0.5f, luxel );
1483 /* debugging code */
1484 //% luxel[ 2 ] += 64.0f;
1487 VectorCopy( luxel, aLuxel );
1488 VectorCopy( luxel, bLuxel );
1501 determines if a single luxel is can be approximated with the interpolated vertex rgba
1504 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv )
1506 int i, x, y, d, lightmapNum;
1508 vec3_t color, vertexColor;
1509 byte cb[ 4 ], vcb[ 4 ];
1512 /* find luxel xy coords */
1513 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1514 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1517 else if( x >= lm->w )
1521 else if( y >= lm->h )
1525 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1528 if( lm->styles[ lightmapNum ] == LS_NONE )
1532 luxel = BSP_LUXEL( lightmapNum, x, y );
1534 /* ignore occluded luxels */
1535 if( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f )
1538 /* copy, set min color and compare */
1539 VectorCopy( luxel, color );
1540 VectorCopy( dv->color[ 0 ], vertexColor );
1542 /* styles are not affected by minlight */
1543 if( lightmapNum == 0 )
1545 for( i = 0; i < 3; i++ )
1548 if( color[ i ] < minLight[ i ] )
1549 color[ i ] = minLight[ i ];
1550 if( vertexColor[ i ] < minLight[ i ] ) /* note NOT minVertexLight */
1551 vertexColor[ i ] = minLight[ i ];
1556 ColorToBytes( color, cb, 1.0f );
1557 ColorToBytes( vertexColor, vcb, 1.0f );
1560 for( i = 0; i < 3; i++ )
1562 d = cb[ i ] - vcb[ i ];
1565 if( d > approximateTolerance )
1570 /* close enough for the girls i date */
1577 ApproximateTriangle()
1578 determines if a single triangle can be approximated with vertex rgba
1581 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] )
1583 bspDrawVert_t mid, *dv2[ 3 ];
1587 /* approximate the vertexes */
1588 if( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse )
1590 if( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse )
1592 if( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse )
1595 /* subdivide calc */
1598 float dx, dy, dist, maxDist;
1601 /* find the longest edge and split it */
1604 for( i = 0; i < 3; i++ )
1606 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 0 ];
1607 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 1 ];
1608 dist = sqrt( (dx * dx) + (dy * dy) );
1609 if( dist > maxDist )
1616 /* try to early out */
1617 if( i < 0 || maxDist < subdivideThreshold )
1621 /* split the longest edge and map it */
1622 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
1623 if( ApproximateLuxel( lm, &mid ) == qfalse )
1626 /* recurse to first triangle */
1627 VectorCopy( dv, dv2 );
1629 if( ApproximateTriangle_r( lm, dv2 ) == qfalse )
1632 /* recurse to second triangle */
1633 VectorCopy( dv, dv2 );
1634 dv2[ (max + 1) % 3 ] = ∣
1635 return ApproximateTriangle_r( lm, dv2 );
1641 ApproximateLightmap()
1642 determines if a raw lightmap can be approximated sufficiently with vertex colors
1645 static qboolean ApproximateLightmap( rawLightmap_t *lm )
1647 int n, num, i, x, y, pw[ 5 ], r;
1648 bspDrawSurface_t *ds;
1649 surfaceInfo_t *info;
1650 mesh_t src, *subdivided, *mesh;
1651 bspDrawVert_t *verts, *dv[ 3 ];
1652 qboolean approximated;
1655 /* approximating? */
1656 if( approximateTolerance <= 0 )
1659 /* test for jmonroe */
1661 /* don't approx lightmaps with styled twins */
1662 if( lm->numStyledTwins > 0 )
1665 /* don't approx lightmaps with styles */
1666 for( i = 1; i < MAX_LIGHTMAPS; i++ )
1668 if( lm->styles[ i ] != LS_NONE )
1673 /* assume reduced until shadow detail is found */
1674 approximated = qtrue;
1676 /* walk the list of surfaces on this raw lightmap */
1677 for( n = 0; n < lm->numLightSurfaces; n++ )
1680 num = lightSurfaces[ lm->firstLightSurface + n ];
1681 ds = &bspDrawSurfaces[ num ];
1682 info = &surfaceInfos[ num ];
1684 /* assume not-reduced initially */
1685 info->approximated = qfalse;
1687 /* bail if lightmap doesn't match up */
1688 if( info->lm != lm )
1691 /* bail if not vertex lit */
1692 if( info->si->noVertexLight )
1695 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1696 if( (info->maxs[ 0 ] - info->mins[ 0 ]) <= (2.0f * info->sampleSize) &&
1697 (info->maxs[ 1 ] - info->mins[ 1 ]) <= (2.0f * info->sampleSize) &&
1698 (info->maxs[ 2 ] - info->mins[ 2 ]) <= (2.0f * info->sampleSize) )
1700 info->approximated = qtrue;
1701 numSurfsVertexForced++;
1705 /* handle the triangles */
1706 switch( ds->surfaceType )
1710 verts = yDrawVerts + ds->firstVert;
1712 /* map the triangles */
1713 info->approximated = qtrue;
1714 for( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1716 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1717 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1718 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1719 info->approximated = ApproximateTriangle_r( lm, dv );
1724 /* make a mesh from the drawsurf */
1725 src.width = ds->patchWidth;
1726 src.height = ds->patchHeight;
1727 src.verts = &yDrawVerts[ ds->firstVert ];
1728 //% subdivided = SubdivideMesh( src, 8, 512 );
1729 subdivided = SubdivideMesh2( src, info->patchIterations );
1731 /* fit it to the curve and remove colinear verts on rows/columns */
1732 PutMeshOnCurve( *subdivided );
1733 mesh = RemoveLinearMeshColumnsRows( subdivided );
1734 FreeMesh( subdivided );
1737 verts = mesh->verts;
1739 /* map the mesh quads */
1740 info->approximated = qtrue;
1741 for( y = 0; y < (mesh->height - 1) && info->approximated; y++ )
1743 for( x = 0; x < (mesh->width - 1) && info->approximated; x++ )
1746 pw[ 0 ] = x + (y * mesh->width);
1747 pw[ 1 ] = x + ((y + 1) * mesh->width);
1748 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1749 pw[ 3 ] = x + 1 + (y * mesh->width);
1750 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1755 /* get drawverts and map first triangle */
1756 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1757 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1758 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1759 info->approximated = ApproximateTriangle_r( lm, dv );
1761 /* get drawverts and map second triangle */
1762 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1763 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1764 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1765 if( info->approximated )
1766 info->approximated = ApproximateTriangle_r( lm, dv );
1779 if( info->approximated == qfalse )
1780 approximated = qfalse;
1782 numSurfsVertexApproximated++;
1786 return approximated;
1792 TestOutLightmapStamp()
1793 tests a stamp on a given lightmap for validity
1796 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y )
1798 int sx, sy, ox, oy, offset;
1803 if( x < 0 || y < 0 || (x + lm->w) > olm->customWidth || (y + lm->h) > olm->customHeight )
1806 /* solid lightmaps test a 1x1 stamp */
1807 if( lm->solid[ lightmapNum ] )
1809 offset = (y * olm->customWidth) + x;
1810 if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1815 /* test the stamp */
1816 for( sy = 0; sy < lm->h; sy++ )
1818 for( sx = 0; sx < lm->w; sx++ )
1821 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1822 if( luxel[ 0 ] < 0.0f )
1825 /* get bsp lightmap coords and test */
1828 offset = (oy * olm->customWidth) + ox;
1829 if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1834 /* stamp is empty */
1842 sets up an output lightmap
1845 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm )
1848 if( lm == NULL || olm == NULL )
1851 /* is this a "normal" bsp-stored lightmap? */
1852 if( (lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize) || externalLightmaps )
1854 olm->lightmapNum = numBSPLightmaps;
1857 /* lightmaps are interleaved with light direction maps */
1862 olm->lightmapNum = -3;
1864 /* set external lightmap number */
1865 olm->extLightmapNum = -1;
1868 olm->numLightmaps = 0;
1869 olm->customWidth = lm->customWidth;
1870 olm->customHeight = lm->customHeight;
1871 olm->freeLuxels = olm->customWidth * olm->customHeight;
1872 olm->numShaders = 0;
1874 /* allocate buffers */
1875 olm->lightBits = safe_malloc( (olm->customWidth * olm->customHeight / 8) + 8 );
1876 memset( olm->lightBits, 0, (olm->customWidth * olm->customHeight / 8) + 8 );
1877 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1878 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1881 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1882 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1890 for a given surface lightmap, find output lightmap pages and positions for it
1893 static void FindOutLightmaps( rawLightmap_t *lm )
1895 int i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
1897 surfaceInfo_t *info;
1898 float *luxel, *deluxel;
1899 vec3_t color, direction;
1904 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1905 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1906 lm->outLightmapNums[ lightmapNum ] = -3;
1908 /* can this lightmap be approximated with vertex color? */
1909 if( ApproximateLightmap( lm ) )
1913 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1916 if( lm->styles[ lightmapNum ] == LS_NONE )
1919 /* don't store twinned lightmaps */
1920 if( lm->twins[ lightmapNum ] != NULL )
1923 /* if this is a styled lightmap, try some normalized locations first */
1925 if( lightmapNum > 0 && outLightmaps != NULL )
1928 for( j = 0; j < 2; j++ )
1930 /* try identical position */
1931 for( i = 0; i < numOutLightmaps; i++ )
1933 /* get the output lightmap */
1934 olm = &outLightmaps[ i ];
1936 /* simple early out test */
1937 if( olm->freeLuxels < lm->used )
1940 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
1941 if( olm->customWidth != lm->customWidth ||
1942 olm->customHeight != lm->customHeight )
1948 x = lm->lightmapX[ 0 ];
1949 y = lm->lightmapY[ 0 ];
1950 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1956 for( sy = -1; sy <= 1; sy++ )
1958 for( sx = -1; sx <= 1; sx++ )
1960 x = lm->lightmapX[ 0 ] + sx * (olm->customWidth >> 1); //% lm->w;
1961 y = lm->lightmapY[ 0 ] + sy * (olm->customHeight >> 1); //% lm->h;
1962 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1982 /* try normal placement algorithm */
1989 /* walk the list of lightmap pages */
1990 for( i = 0; i < numOutLightmaps; i++ )
1992 /* get the output lightmap */
1993 olm = &outLightmaps[ i ];
1995 /* simple early out test */
1996 if( olm->freeLuxels < lm->used )
1999 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2000 if( olm->customWidth != lm->customWidth ||
2001 olm->customHeight != lm->customHeight )
2005 if( lm->solid[ lightmapNum ] )
2007 xMax = olm->customWidth;
2008 yMax = olm->customHeight;
2012 xMax = (olm->customWidth - lm->w) + 1;
2013 yMax = (olm->customHeight - lm->h) + 1;
2016 /* walk the origin around the lightmap */
2017 for( y = 0; y < yMax; y++ )
2019 for( x = 0; x < xMax; x++ )
2021 /* find a fine tract of lauhnd */
2022 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2044 /* allocate two new output lightmaps */
2045 numOutLightmaps += 2;
2046 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2047 if( outLightmaps != NULL && numOutLightmaps > 2 )
2049 memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );
2050 free( outLightmaps );
2054 /* initialize both out lightmaps */
2055 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
2056 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
2058 /* set out lightmap */
2059 i = numOutLightmaps - 2;
2060 olm = &outLightmaps[ i ];
2062 /* set stamp xy origin to the first surface lightmap */
2063 if( lightmapNum > 0 )
2065 x = lm->lightmapX[ 0 ];
2066 y = lm->lightmapY[ 0 ];
2070 /* if this is a style-using lightmap, it must be exported */
2071 if( lightmapNum > 0 && game->load != LoadRBSPFile )
2072 olm->extLightmapNum = 0;
2074 /* add the surface lightmap to the bsp lightmap */
2075 lm->outLightmapNums[ lightmapNum ] = i;
2076 lm->lightmapX[ lightmapNum ] = x;
2077 lm->lightmapY[ lightmapNum ] = y;
2078 olm->numLightmaps++;
2081 for( i = 0; i < lm->numLightSurfaces; i++ )
2083 /* get surface info */
2084 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2086 /* test for shader */
2087 for( j = 0; j < olm->numShaders; j++ )
2089 if( olm->shaders[ j ] == info->si )
2093 /* if it doesn't exist, add it */
2094 if( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS )
2096 olm->shaders[ olm->numShaders ] = info->si;
2098 numLightmapShaders++;
2103 if( lm->solid[ lightmapNum ] )
2114 /* mark the bits used */
2115 for( y = 0; y < yMax; y++ )
2117 for( x = 0; x < xMax; x++ )
2120 luxel = BSP_LUXEL( lightmapNum, x, y );
2121 deluxel = BSP_DELUXEL( x, y );
2122 if( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ])
2125 /* set minimum light */
2126 if( lm->solid[ lightmapNum ] )
2129 VectorSet( color, 255.0f, 0.0f, 0.0f );
2131 VectorCopy( lm->solidColor[ lightmapNum ], color );
2134 VectorCopy( luxel, color );
2136 /* styles are not affected by minlight */
2137 if( lightmapNum == 0 )
2139 for( i = 0; i < 3; i++ )
2141 if( color[ i ] < minLight[ i ] )
2142 color[ i ] = minLight[ i ];
2146 /* get bsp lightmap coords */
2147 ox = x + lm->lightmapX[ lightmapNum ];
2148 oy = y + lm->lightmapY[ lightmapNum ];
2149 offset = (oy * olm->customWidth) + ox;
2151 /* flag pixel as used */
2152 olm->lightBits[ offset >> 3 ] |= (1 << (offset & 7));
2156 pixel = olm->bspLightBytes + (((oy * olm->customWidth) + ox) * 3);
2157 ColorToBytes( color, pixel, lm->brightness );
2159 /* store direction */
2162 /* normalize average light direction */
2163 if( VectorNormalize( deluxel, direction ) )
2165 /* encode [-1,1] in [0,255] */
2166 pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
2167 for( i = 0; i < 3; i++ )
2169 temp = (direction[ i ] + 1.0f) * 127.5f;
2172 else if( temp > 255 )
2187 CompareRawLightmap()
2188 compare function for qsort()
2191 static int CompareRawLightmap( const void *a, const void *b )
2193 rawLightmap_t *alm, *blm;
2194 surfaceInfo_t *aInfo, *bInfo;
2199 alm = &rawLightmaps[ *((int*) a) ];
2200 blm = &rawLightmaps[ *((int*) b) ];
2202 /* get min number of surfaces */
2203 min = (alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces);
2206 for( i = 0; i < min; i++ )
2208 /* get surface info */
2209 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2210 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2212 /* compare shader names */
2213 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2218 /* test style count */
2220 for( i = 0; i < MAX_LIGHTMAPS; i++ )
2221 diff += blm->styles[ i ] - alm->styles[ i ];
2226 diff = (blm->w * blm->h) - (alm->w * alm->h);
2230 /* must be equivalent */
2237 StoreSurfaceLightmaps()
2238 stores the surface lightmaps into the bsp as byte rgb triplets
2241 void StoreSurfaceLightmaps( void )
2243 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2244 int style, size, lightmapNum, lightmapNum2;
2245 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2246 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2247 float *deluxel, *bspDeluxel, *bspDeluxel2;
2249 int numUsed, numTwins, numTwinLuxels, numStored;
2250 float lmx, lmy, efficiency;
2252 bspDrawSurface_t *ds, *parent, dsTemp;
2253 surfaceInfo_t *info;
2254 rawLightmap_t *lm, *lm2;
2256 bspDrawVert_t *dv, *ydv, *dvParent;
2257 char dirname[ 1024 ], filename[ 1024 ];
2259 char lightmapName[ 128 ];
2260 char *rgbGenValues[ 256 ];
2261 char *alphaGenValues[ 256 ];
2265 Sys_Printf( "--- StoreSurfaceLightmaps ---\n");
2268 strcpy( dirname, source );
2269 StripExtension( dirname );
2270 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2271 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2273 /* -----------------------------------------------------------------
2274 average the sampled luxels into the bsp luxels
2275 ----------------------------------------------------------------- */
2278 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2280 /* walk the list of raw lightmaps */
2284 numSolidLightmaps = 0;
2285 for( i = 0; i < numRawLightmaps; i++ )
2288 lm = &rawLightmaps[ i ];
2290 /* walk individual lightmaps */
2291 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2294 if( lm->superLuxels[ lightmapNum ] == NULL )
2297 /* allocate bsp luxel storage */
2298 if( lm->bspLuxels[ lightmapNum ] == NULL )
2300 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2301 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2302 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2305 /* allocate radiosity lightmap storage */
2308 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2309 if( lm->radLuxels[ lightmapNum ] == NULL )
2310 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2311 memset( lm->radLuxels[ lightmapNum ], 0, size );
2314 /* average supersampled luxels */
2315 for( y = 0; y < lm->h; y++ )
2317 for( x = 0; x < lm->w; x++ )
2321 occludedSamples = 0.0f;
2323 VectorClear( sample );
2324 VectorClear( occludedSample );
2325 VectorClear( dirSample );
2326 for( ly = 0; ly < superSample; ly++ )
2328 for( lx = 0; lx < superSample; lx++ )
2331 sx = x * superSample + lx;
2332 sy = y * superSample + ly;
2333 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2334 deluxel = SUPER_DELUXEL( sx, sy );
2335 normal = SUPER_NORMAL( sx, sy );
2336 cluster = SUPER_CLUSTER( sx, sy );
2338 /* sample deluxemap */
2339 if( deluxemap && lightmapNum == 0 )
2340 VectorAdd( dirSample, deluxel, dirSample );
2342 /* keep track of used/occluded samples */
2343 if( *cluster != CLUSTER_UNMAPPED )
2346 /* handle lightmap border? */
2347 if( lightmapBorder && (sx == 0 || sx == (lm->sw - 1) || sy == 0 || sy == (lm->sh - 1) ) && luxel[ 3 ] > 0.0f )
2349 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2354 else if( debug && *cluster < 0 )
2356 if( *cluster == CLUSTER_UNMAPPED )
2357 VectorSet( luxel, 255, 204, 0 );
2358 else if( *cluster == CLUSTER_OCCLUDED )
2359 VectorSet( luxel, 255, 0, 255 );
2360 else if( *cluster == CLUSTER_FLOODED )
2361 VectorSet( luxel, 0, 32, 255 );
2362 VectorAdd( occludedSample, luxel, occludedSample );
2363 occludedSamples += 1.0f;
2366 /* normal luxel handling */
2367 else if( luxel[ 3 ] > 0.0f )
2369 /* handle lit or flooded luxels */
2370 if( *cluster > 0 || *cluster == CLUSTER_FLOODED )
2372 VectorAdd( sample, luxel, sample );
2373 samples += luxel[ 3 ];
2376 /* handle occluded or unmapped luxels */
2379 VectorAdd( occludedSample, luxel, occludedSample );
2380 occludedSamples += luxel[ 3 ];
2383 /* handle style debugging */
2384 if( debug && lightmapNum > 0 && x < 2 && y < 2 )
2386 VectorCopy( debugColors[ 0 ], sample );
2393 /* only use occluded samples if necessary */
2394 if( samples <= 0.0f )
2396 VectorCopy( occludedSample, sample );
2397 samples = occludedSamples;
2401 luxel = SUPER_LUXEL( lightmapNum, x, y );
2402 deluxel = SUPER_DELUXEL( x, y );
2404 /* store light direction */
2405 if( deluxemap && lightmapNum == 0 )
2406 VectorCopy( dirSample, deluxel );
2408 /* store the sample back in super luxels */
2409 if( samples > 0.01f )
2411 VectorScale( sample, (1.0f / samples), luxel );
2415 /* if any samples were mapped in any way, store ambient color */
2416 else if( mappedSamples > 0 )
2418 if( lightmapNum == 0 )
2419 VectorCopy( ambientColor, luxel );
2421 VectorClear( luxel );
2425 /* store a bogus value to be fixed later */
2428 VectorClear( luxel );
2436 ClearBounds( colorMins, colorMaxs );
2438 /* clean up and store into bsp luxels */
2439 for( y = 0; y < lm->h; y++ )
2441 for( x = 0; x < lm->w; x++ )
2444 luxel = SUPER_LUXEL( lightmapNum, x, y );
2445 deluxel = SUPER_DELUXEL( x, y );
2447 /* copy light direction */
2448 if( deluxemap && lightmapNum == 0 )
2449 VectorCopy( deluxel, dirSample );
2451 /* is this a valid sample? */
2452 if( luxel[ 3 ] > 0.0f )
2454 VectorCopy( luxel, sample );
2455 samples = luxel[ 3 ];
2459 /* fix negative samples */
2460 for( j = 0; j < 3; j++ )
2462 if( sample[ j ] < 0.0f )
2468 /* nick an average value from the neighbors */
2469 VectorClear( sample );
2470 VectorClear( dirSample );
2473 /* fixme: why is this disabled?? */
2474 for( sy = (y - 1); sy <= (y + 1); sy++ )
2476 if( sy < 0 || sy >= lm->h )
2479 for( sx = (x - 1); sx <= (x + 1); sx++ )
2481 if( sx < 0 || sx >= lm->w || (sx == x && sy == y) )
2484 /* get neighbor's particulars */
2485 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2486 if( luxel[ 3 ] < 0.0f )
2488 VectorAdd( sample, luxel, sample );
2489 samples += luxel[ 3 ];
2494 if( samples == 0.0f )
2496 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2504 /* fix negative samples */
2505 for( j = 0; j < 3; j++ )
2507 if( sample[ j ] < 0.0f )
2513 /* scale the sample */
2514 VectorScale( sample, (1.0f / samples), sample );
2516 /* store the sample in the radiosity luxels */
2519 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2520 VectorCopy( sample, radLuxel );
2522 /* if only storing bounced light, early out here */
2523 if( bounceOnly && !bouncing )
2527 /* store the sample in the bsp luxels */
2528 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2529 bspDeluxel = BSP_DELUXEL( x, y );
2531 VectorAdd( bspLuxel, sample, bspLuxel );
2532 if( deluxemap && lightmapNum == 0 )
2533 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2535 /* add color to bounds for solid checking */
2536 if( samples > 0.0f )
2537 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2541 /* set solid color */
2542 lm->solid[ lightmapNum ] = qfalse;
2543 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2544 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2546 /* nocollapse prevents solid lightmaps */
2547 if( noCollapse == qfalse )
2549 /* check solid color */
2550 VectorSubtract( colorMaxs, colorMins, sample );
2551 if( (sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON) ||
2552 (lm->w <= 2 && lm->h <= 2) ) /* small lightmaps get forced to solid color */
2555 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2556 lm->solid[ lightmapNum ] = qtrue;
2557 numSolidLightmaps++;
2560 /* if all lightmaps aren't solid, then none of them are solid */
2561 if( lm->solid[ lightmapNum ] != lm->solid[ 0 ] )
2563 for( y = 0; y < MAX_LIGHTMAPS; y++ )
2565 if( lm->solid[ y ] )
2566 numSolidLightmaps--;
2567 lm->solid[ y ] = qfalse;
2572 /* wrap bsp luxels if necessary */
2575 for( y = 0; y < lm->h; y++ )
2577 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2578 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2579 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2580 VectorScale( bspLuxel, 0.5f, bspLuxel );
2581 VectorCopy( bspLuxel, bspLuxel2 );
2582 if( deluxemap && lightmapNum == 0 )
2584 bspDeluxel = BSP_DELUXEL( 0, y );
2585 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2586 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2587 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2588 VectorCopy( bspDeluxel, bspDeluxel2 );
2594 for( x = 0; x < lm->w; x++ )
2596 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2597 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2598 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2599 VectorScale( bspLuxel, 0.5f, bspLuxel );
2600 VectorCopy( bspLuxel, bspLuxel2 );
2601 if( deluxemap && lightmapNum == 0 )
2603 bspDeluxel = BSP_DELUXEL( x, 0 );
2604 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2605 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2606 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2607 VectorCopy( bspDeluxel, bspDeluxel2 );
2614 /* -----------------------------------------------------------------
2615 collapse non-unique lightmaps
2616 ----------------------------------------------------------------- */
2618 if( noCollapse == qfalse && deluxemap == qfalse )
2621 Sys_FPrintf( SYS_VRB, "collapsing..." );
2623 /* set all twin refs to null */
2624 for( i = 0; i < numRawLightmaps; i++ )
2626 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2628 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2629 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2630 rawLightmaps[ i ].numStyledTwins = 0;
2634 /* walk the list of raw lightmaps */
2635 for( i = 0; i < numRawLightmaps; i++ )
2638 lm = &rawLightmaps[ i ];
2640 /* walk lightmaps */
2641 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2644 if( lm->bspLuxels[ lightmapNum ] == NULL ||
2645 lm->twins[ lightmapNum ] != NULL )
2648 /* find all lightmaps that are virtually identical to this one */
2649 for( j = i + 1; j < numRawLightmaps; j++ )
2652 lm2 = &rawLightmaps[ j ];
2654 /* walk lightmaps */
2655 for( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
2658 if( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
2659 lm2->twins[ lightmapNum2 ] != NULL )
2663 if( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2665 /* merge and set twin */
2666 if( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2668 lm2->twins[ lightmapNum2 ] = lm;
2669 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
2671 numTwinLuxels += (lm->w * lm->h);
2673 /* count styled twins */
2674 if( lightmapNum > 0 )
2675 lm->numStyledTwins++;
2684 /* -----------------------------------------------------------------
2685 sort raw lightmaps by shader
2686 ----------------------------------------------------------------- */
2689 Sys_FPrintf( SYS_VRB, "sorting..." );
2691 /* allocate a new sorted list */
2692 if( sortLightmaps == NULL )
2693 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
2695 /* fill it out and sort it */
2696 for( i = 0; i < numRawLightmaps; i++ )
2697 sortLightmaps[ i ] = i;
2698 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
2700 /* -----------------------------------------------------------------
2701 allocate output lightmaps
2702 ----------------------------------------------------------------- */
2705 Sys_FPrintf( SYS_VRB, "allocating..." );
2707 /* kill all existing output lightmaps */
2708 if( outLightmaps != NULL )
2710 for( i = 0; i < numOutLightmaps; i++ )
2712 free( outLightmaps[ i ].lightBits );
2713 free( outLightmaps[ i ].bspLightBytes );
2715 free( outLightmaps );
2716 outLightmaps = NULL;
2719 numLightmapShaders = 0;
2720 numOutLightmaps = 0;
2721 numBSPLightmaps = 0;
2722 numExtLightmaps = 0;
2724 /* find output lightmap */
2725 for( i = 0; i < numRawLightmaps; i++ )
2727 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2728 FindOutLightmaps( lm );
2731 /* set output numbers in twinned lightmaps */
2732 for( i = 0; i < numRawLightmaps; i++ )
2735 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2737 /* walk lightmaps */
2738 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2741 lm2 = lm->twins[ lightmapNum ];
2744 lightmapNum2 = lm->twinNums[ lightmapNum ];
2746 /* find output lightmap from twin */
2747 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
2748 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
2749 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
2753 /* -----------------------------------------------------------------
2754 store output lightmaps
2755 ----------------------------------------------------------------- */
2758 Sys_FPrintf( SYS_VRB, "storing..." );
2760 /* count the bsp lightmaps and allocate space */
2761 if( bspLightBytes != NULL )
2762 free( bspLightBytes );
2763 if( numBSPLightmaps == 0 || externalLightmaps )
2765 numBSPLightBytes = 0;
2766 bspLightBytes = NULL;
2770 numBSPLightBytes = (numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3);
2771 bspLightBytes = safe_malloc( numBSPLightBytes );
2772 memset( bspLightBytes, 0, numBSPLightBytes );
2775 /* walk the list of output lightmaps */
2776 for( i = 0; i < numOutLightmaps; i++ )
2778 /* get output lightmap */
2779 olm = &outLightmaps[ i ];
2781 /* is this a valid bsp lightmap? */
2782 if( olm->lightmapNum >= 0 && !externalLightmaps )
2784 /* copy lighting data */
2785 lb = bspLightBytes + (olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3);
2786 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
2788 /* copy direction data */
2791 lb = bspLightBytes + ((olm->lightmapNum + 1) * game->lightmapSize * game->lightmapSize * 3);
2792 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
2796 /* external lightmap? */
2797 if( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps )
2799 /* make a directory for the lightmaps */
2802 /* set external lightmap number */
2803 olm->extLightmapNum = numExtLightmaps;
2805 /* write lightmap */
2806 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2807 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2808 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
2811 /* write deluxemap */
2814 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2815 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2816 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
2819 if( debugDeluxemap )
2820 olm->extLightmapNum++;
2825 if( numExtLightmaps > 0 )
2826 Sys_FPrintf( SYS_VRB, "\n" );
2828 /* delete unused external lightmaps */
2829 for( i = numExtLightmaps; i; i++ )
2831 /* determine if file exists */
2832 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
2833 if( !FileExists( filename ) )
2840 /* -----------------------------------------------------------------
2841 project the lightmaps onto the bsp surfaces
2842 ----------------------------------------------------------------- */
2845 Sys_FPrintf( SYS_VRB, "projecting..." );
2847 /* walk the list of surfaces */
2848 for( i = 0; i < numBSPDrawSurfaces; i++ )
2850 /* get the surface and info */
2851 ds = &bspDrawSurfaces[ i ];
2852 info = &surfaceInfos[ i ];
2856 /* handle surfaces with identical parent */
2857 if( info->parentSurfaceNum >= 0 )
2859 /* preserve original data and get parent */
2860 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
2861 memcpy( &dsTemp, ds, sizeof( *ds ) );
2863 /* overwrite child with parent data */
2864 memcpy( ds, parent, sizeof( *ds ) );
2866 /* restore key parts */
2867 ds->fogNum = dsTemp.fogNum;
2868 ds->firstVert = dsTemp.firstVert;
2869 ds->firstIndex = dsTemp.firstIndex;
2870 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
2872 /* set vertex data */
2873 dv = &bspDrawVerts[ ds->firstVert ];
2874 dvParent = &bspDrawVerts[ parent->firstVert ];
2875 for( j = 0; j < ds->numVerts; j++ )
2877 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
2878 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
2885 /* handle vertex lit or approximated surfaces */
2886 else if( lm == NULL || lm->outLightmapNums[ 0 ] < 0 )
2888 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2890 ds->lightmapNum[ lightmapNum ] = -3;
2891 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
2895 /* handle lightmapped surfaces */
2898 /* walk lightmaps */
2899 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2902 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2904 /* handle unused style */
2905 if( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
2907 ds->lightmapNum[ lightmapNum ] = -3;
2911 /* get output lightmap */
2912 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
2914 /* set bsp lightmap number */
2915 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
2917 /* deluxemap debugging makes the deluxemap visible */
2918 if( deluxemap && debugDeluxemap && lightmapNum == 0 )
2919 ds->lightmapNum[ lightmapNum ]++;
2921 /* calc lightmap origin in texture space */
2922 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
2923 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
2925 /* calc lightmap st coords */
2926 dv = &bspDrawVerts[ ds->firstVert ];
2927 ydv = &yDrawVerts[ ds->firstVert ];
2928 for( j = 0; j < ds->numVerts; j++ )
2930 if( lm->solid[ lightmapNum ] )
2932 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (0.5f / (float) olm->customWidth);
2933 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (0.5f / (float) olm->customWidth);
2937 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (ydv[ j ].lightmap[ 0 ][ 0 ] / (superSample * olm->customWidth));
2938 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (ydv[ j ].lightmap[ 0 ][ 1 ] / (superSample * olm->customHeight));
2944 /* store vertex colors */
2945 dv = &bspDrawVerts[ ds->firstVert ];
2946 for( j = 0; j < ds->numVerts; j++ )
2948 /* walk lightmaps */
2949 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2951 /* handle unused style */
2952 if( ds->vertexStyles[ lightmapNum ] == LS_NONE )
2953 VectorClear( color );
2956 /* get vertex color */
2957 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
2958 VectorCopy( luxel, color );
2960 /* set minimum light */
2961 if( lightmapNum == 0 )
2963 for( k = 0; k < 3; k++ )
2964 if( color[ k ] < minVertexLight[ k ] )
2965 color[ k ] = minVertexLight[ k ];
2969 /* store to bytes */
2970 if( !info->si->noVertexLight )
2971 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
2975 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
2976 if( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) //% info->si->styleMarker > 0 )
2979 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
2983 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
2984 dv = &bspDrawVerts[ ds->firstVert ];
2986 /* depthFunc equal? */
2987 if( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED )
2992 /* generate stages for styled lightmaps */
2993 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2996 style = lm->styles[ lightmapNum ];
2997 if( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
3000 /* get output lightmap */
3001 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3004 if( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] )
3005 strcpy( lightmapName, "$lightmap" );
3007 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3009 /* get rgbgen string */
3010 if( rgbGenValues[ style ] == NULL )
3012 sprintf( key, "_style%drgbgen", style );
3013 rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3014 if( rgbGenValues[ style ][ 0 ] == '\0' )
3015 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3018 if( rgbGenValues[ style ][ 0 ] != '\0' )
3019 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3023 /* get alphagen string */
3024 if( alphaGenValues[ style ] == NULL )
3026 sprintf( key, "_style%dalphagen", style );
3027 alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3029 if( alphaGenValues[ style ][ 0 ] != '\0' )
3030 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3032 alphaGen[ 0 ] = '\0';
3034 /* calculate st offset */
3035 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3036 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3038 /* create additional stage */
3039 if( lmx == 0.0f && lmy == 0.0f )
3041 sprintf( styleStage, "\t{\n"
3042 "\t\tmap %s\n" /* lightmap */
3043 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3044 "%s" /* depthFunc equal */
3047 "\t\ttcGen lightmap\n"
3050 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
3056 sprintf( styleStage, "\t{\n"
3057 "\t\tmap %s\n" /* lightmap */
3058 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3059 "%s" /* depthFunc equal */
3062 "\t\ttcGen lightmap\n"
3063 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3066 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
3074 strcat( styleStages, styleStage );
3077 /* create custom shader */
3078 if( info->si->styleMarker == 2 )
3079 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3081 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3083 /* emit remap command */
3084 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3087 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3088 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3089 //% Sys_Printf( ")\n" );
3092 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3093 else if( olm != NULL && lm != NULL && !externalLightmaps &&
3094 (olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize) )
3096 /* get output lightmap */
3097 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3099 /* do some name mangling */
3100 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3102 /* create custom shader */
3103 csi = CustomShader( info->si, "$lightmap", lightmapName );
3106 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3107 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3108 //% Sys_Printf( ")\n" );
3111 /* use the normal plain-jane shader */
3113 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3117 Sys_FPrintf( SYS_VRB, "done.\n" );
3119 /* calc num stored */
3120 numStored = numBSPLightBytes / 3;
3121 efficiency = (numStored <= 0)
3123 : (float) numUsed / (float) numStored;
3126 Sys_Printf( "%9d luxels used\n", numUsed );
3127 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3128 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3129 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3130 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3131 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3132 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3133 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3134 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3136 /* write map shader file */
3137 WriteMapShaderFile();