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 if(sampleSize != lm->sampleSize)
690 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",
701 /* set actual sample size */
702 lm->actualSampleSize = sampleSize;
704 /* fixme: copy rounded mins/maxes to lightmap record? */
705 if( lm->plane == NULL )
707 VectorCopy( mins, lm->mins );
708 VectorCopy( maxs, lm->maxs );
709 VectorCopy( mins, origin );
712 /* set lightmap origin */
713 VectorCopy( lm->mins, origin );
715 /* make absolute axis */
716 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
717 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
718 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
720 /* clear out lightmap vectors */
721 memset( vecs, 0, sizeof( vecs ) );
723 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
724 if( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] )
729 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
730 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
732 else if( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] )
737 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
738 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
745 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
746 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
749 /* check for bogus axis */
750 if( faxis[ axisNum ] == 0.0f )
752 Sys_Printf( "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
757 /* store the axis number in the lightmap */
758 lm->axisNum = axisNum;
760 /* walk the list of surfaces on this raw lightmap */
761 for( n = 0; n < lm->numLightSurfaces; n++ )
764 num2 = lightSurfaces[ lm->firstLightSurface + n ];
765 ds2 = &bspDrawSurfaces[ num2 ];
766 info2 = &surfaceInfos[ num2 ];
767 verts = &yDrawVerts[ ds2->firstVert ];
769 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
770 for( i = 0; i < ds2->numVerts; i++ )
772 VectorSubtract( verts[ i ].xyz, origin, delta );
773 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
774 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
775 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
776 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
778 if( s > (float) lm->w || t > (float) lm->h )
780 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
781 s, lm->w, t, lm->h );
786 /* get first drawsurface */
787 num2 = lightSurfaces[ lm->firstLightSurface ];
788 ds2 = &bspDrawSurfaces[ num2 ];
789 info2 = &surfaceInfos[ num2 ];
790 verts = &yDrawVerts[ ds2->firstVert ];
792 /* calculate lightmap origin */
793 if( VectorLength( ds2->lightmapVecs[ 2 ] ) )
794 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
796 VectorCopy( lm->axis, plane );
797 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
799 VectorCopy( origin, lm->origin );
800 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
801 d /= plane[ axisNum ];
802 lm->origin[ axisNum ] -= d;
805 VectorCopy( lm->origin, ds->lightmapOrigin );
807 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
808 if( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) /* ydnar: can't remember what exactly i was thinking here... */
810 /* allocate space for the vectors */
811 lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
812 memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
813 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
815 /* project stepped lightmap blocks and subtract to get planevecs */
816 for( i = 0; i < 2; i++ )
818 len = VectorNormalize( vecs[ i ], normalized );
819 VectorScale( normalized, (1.0 / len), lm->vecs[ i ] );
820 d = DotProduct( lm->vecs[ i ], plane );
821 d /= plane[ axisNum ];
822 lm->vecs[ i ][ axisNum ] -= d;
827 /* lightmap vectors are useless on a non-planar surface */
832 if( ds->surfaceType == MST_PATCH )
834 numPatchesLightmapped++;
835 if( lm->plane != NULL )
836 numPlanarPatchesLightmapped++;
840 if( lm->plane != NULL )
841 numPlanarsLightmapped++;
843 numNonPlanarsLightmapped++;
854 compare function for qsort()
857 static int CompareSurfaceInfo( const void *a, const void *b )
859 surfaceInfo_t *aInfo, *bInfo;
863 /* get surface info */
864 aInfo = &surfaceInfos[ *((int*) a) ];
865 bInfo = &surfaceInfos[ *((int*) b) ];
868 if( aInfo->model < bInfo->model )
870 else if( aInfo->model > bInfo->model )
873 /* then lightmap status */
874 if( aInfo->hasLightmap < bInfo->hasLightmap )
876 else if( aInfo->hasLightmap > bInfo->hasLightmap )
879 /* then lightmap sample size */
880 if( aInfo->sampleSize < bInfo->sampleSize )
882 else if( aInfo->sampleSize > bInfo->sampleSize )
885 /* then lightmap axis */
886 for( i = 0; i < 3; i++ )
888 if( aInfo->axis[ i ] < bInfo->axis[ i ] )
890 else if( aInfo->axis[ i ] > bInfo->axis[ i ] )
895 if( aInfo->plane == NULL && bInfo->plane != NULL )
897 else if( aInfo->plane != NULL && bInfo->plane == NULL )
899 else if( aInfo->plane != NULL && bInfo->plane != NULL )
901 for( i = 0; i < 4; i++ )
903 if( aInfo->plane[ i ] < bInfo->plane[ i ] )
905 else if( aInfo->plane[ i ] > bInfo->plane[ i ] )
910 /* then position in world */
911 for( i = 0; i < 3; i++ )
913 if( aInfo->mins[ i ] < bInfo->mins[ i ] )
915 else if( aInfo->mins[ i ] > bInfo->mins[ i ] )
919 /* these are functionally identical (this should almost never happen) */
926 SetupSurfaceLightmaps()
927 allocates lightmaps for every surface in the bsp that needs one
928 this depends on yDrawVerts being allocated
931 void SetupSurfaceLightmaps( void )
933 int i, j, k, s,num, num2;
936 bspDrawSurface_t *ds, *ds2;
937 surfaceInfo_t *info, *info2;
940 vec3_t mapSize, entityOrigin;
944 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n");
946 /* determine supersample amount */
947 if( superSample < 1 )
949 else if( superSample > 8 )
951 Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
955 /* clear map bounds */
956 ClearBounds( mapMins, mapMaxs );
958 /* allocate a list of surface clusters */
959 numSurfaceClusters = 0;
960 maxSurfaceClusters = numBSPLeafSurfaces;
961 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
962 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
964 /* allocate a list for per-surface info */
965 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
966 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
967 for( i = 0; i < numBSPDrawSurfaces; i++ )
968 surfaceInfos[ i ].childSurfaceNum = -1;
970 /* allocate a list of surface indexes to be sorted */
971 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
972 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
974 /* walk each model in the bsp */
975 for( i = 0; i < numBSPModels; i++ )
978 model = &bspModels[ i ];
980 /* walk the list of surfaces in this model and fill out the info structs */
981 for( j = 0; j < model->numBSPSurfaces; j++ )
983 /* make surface index */
984 num = model->firstBSPSurface + j;
986 /* copy index to sort list */
987 sortSurfaces[ num ] = num;
989 /* get surface and info */
990 ds = &bspDrawSurfaces[ num ];
991 info = &surfaceInfos[ num ];
993 /* set entity origin */
994 if( ds->numVerts > 0 )
995 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
997 VectorClear( entityOrigin );
1000 info->model = model;
1003 info->firstSurfaceCluster = numSurfaceClusters;
1005 /* get extra data */
1006 info->si = GetSurfaceExtraShaderInfo( num );
1007 if( info->si == NULL )
1008 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
1009 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1010 info->entityNum = GetSurfaceExtraEntityNum( num );
1011 info->castShadows = GetSurfaceExtraCastShadows( num );
1012 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1013 info->sampleSize = GetSurfaceExtraSampleSize( num );
1014 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1015 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1016 GetSurfaceExtraLightmapAxis( num, info->axis );
1019 if( info->parentSurfaceNum >= 0 )
1020 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1022 /* determine surface bounds */
1023 ClearBounds( info->mins, info->maxs );
1024 for( k = 0; k < ds->numVerts; k++ )
1026 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1027 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1030 /* find all the bsp clusters the surface falls into */
1031 for( k = 0; k < numBSPLeafs; k++ )
1034 leaf = &bspLeafs[ k ];
1037 if( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1038 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1039 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] )
1042 /* test leaf surfaces */
1043 for( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1045 if( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num )
1047 if( numSurfaceClusters >= maxSurfaceClusters )
1048 Error( "maxSurfaceClusters exceeded" );
1049 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1050 numSurfaceClusters++;
1051 info->numSurfaceClusters++;
1056 /* determine if surface is planar */
1057 if( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f )
1060 info->plane = safe_malloc( 4 * sizeof( float ) );
1061 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1062 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1065 /* determine if surface requires a lightmap */
1066 if( ds->surfaceType == MST_TRIANGLE_SOUP ||
1067 ds->surfaceType == MST_FOLIAGE ||
1068 (info->si->compileFlags & C_VERTEXLIT) )
1069 numSurfsVertexLit++;
1072 numSurfsLightmapped++;
1073 info->hasLightmap = qtrue;
1078 /* find longest map distance */
1079 VectorSubtract( mapMaxs, mapMins, mapSize );
1080 maxMapDistance = VectorLength( mapSize );
1082 /* sort the surfaces info list */
1083 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1085 /* allocate a list of surfaces that would go into raw lightmaps */
1086 numLightSurfaces = 0;
1087 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1088 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1090 /* allocate a list of raw lightmaps */
1091 numRawSuperLuxels = 0;
1092 numRawLightmaps = 0;
1093 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1094 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1096 /* walk the list of sorted surfaces */
1097 for( i = 0; i < numBSPDrawSurfaces; i++ )
1099 /* get info and attempt early out */
1100 num = sortSurfaces[ i ];
1101 ds = &bspDrawSurfaces[ num ];
1102 info = &surfaceInfos[ num ];
1103 if( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 )
1106 /* allocate a new raw lightmap */
1107 lm = &rawLightmaps[ numRawLightmaps ];
1111 lm->splotchFix = info->si->splotchFix;
1112 lm->firstLightSurface = numLightSurfaces;
1113 lm->numLightSurfaces = 0;
1114 lm->sampleSize = info->sampleSize;
1115 lm->actualSampleSize = info->sampleSize;
1116 lm->entityNum = info->entityNum;
1117 lm->recvShadows = info->recvShadows;
1118 lm->brightness = info->si->lmBrightness;
1119 lm->filterRadius = info->si->lmFilterRadius;
1120 VectorCopy( info->axis, lm->axis );
1121 lm->plane = info->plane;
1122 VectorCopy( info->mins, lm->mins );
1123 VectorCopy( info->maxs, lm->maxs );
1125 lm->customWidth = info->si->lmCustomWidth;
1126 lm->customHeight = info->si->lmCustomHeight;
1128 /* add the surface to the raw lightmap */
1129 AddSurfaceToRawLightmap( num, lm );
1132 /* do an exhaustive merge */
1136 /* walk the list of surfaces again */
1138 for( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1140 /* get info and attempt early out */
1141 num2 = sortSurfaces[ j ];
1142 ds2 = &bspDrawSurfaces[ num2 ];
1143 info2 = &surfaceInfos[ num2 ];
1144 if( info2->hasLightmap == qfalse || info2->lm != NULL )
1147 /* add the surface to the raw lightmap */
1148 if( AddSurfaceToRawLightmap( num2, lm ) )
1156 lm->numLightSurfaces--;
1162 /* finish the lightmap and allocate the various buffers */
1163 FinishRawLightmap( lm );
1166 /* allocate vertex luxel storage */
1167 for( k = 0; k < MAX_LIGHTMAPS; k++ )
1169 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1170 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1171 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1172 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1175 /* emit some stats */
1176 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1177 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1178 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1179 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1180 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1181 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1182 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1183 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1189 StitchSurfaceLightmaps()
1190 stitches lightmap edges
1191 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1194 #define MAX_STITCH_CANDIDATES 32
1195 #define MAX_STITCH_LUXELS 64
1197 void StitchSurfaceLightmaps( void )
1199 int i, j, x, y, x2, y2, *cluster, *cluster2,
1200 numStitched, numCandidates, numLuxels, f, fOld, start;
1201 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1202 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1203 sampleSize, average[ 3 ], totalColor, ootc, *luxels[ MAX_STITCH_LUXELS ];
1206 /* disabled for now */
1210 Sys_Printf( "--- StitchSurfaceLightmaps ---\n");
1214 start = I_FloatTime();
1216 /* walk the list of raw lightmaps */
1218 for( i = 0; i < numRawLightmaps; i++ )
1220 /* print pacifier */
1221 f = 10 * i / numRawLightmaps;
1225 Sys_Printf( "%i...", f );
1228 /* get lightmap a */
1229 a = &rawLightmaps[ i ];
1231 /* walk rest of lightmaps */
1233 for( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1235 /* get lightmap b */
1236 b = &rawLightmaps[ j ];
1238 /* test bounding box */
1239 if( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1240 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1241 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] )
1245 c[ numCandidates++ ] = b;
1249 for( y = 0; y < a->sh; y++ )
1251 for( x = 0; x < a->sw; x++ )
1253 /* ignore unmapped/unlit luxels */
1255 cluster = SUPER_CLUSTER( x, y );
1256 if( *cluster == CLUSTER_UNMAPPED )
1258 luxel = SUPER_LUXEL( 0, x, y );
1259 if( luxel[ 3 ] <= 0.0f )
1262 /* get particulars */
1263 origin = SUPER_ORIGIN( x, y );
1264 normal = SUPER_NORMAL( x, y );
1266 /* walk candidate list */
1267 for( j = 0; j < numCandidates; j++ )
1273 /* set samplesize to the smaller of the pair */
1274 sampleSize = 0.5f * (a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize);
1276 /* test bounding box */
1277 if( origin[ 0 ] < (b->mins[ 0 ] - sampleSize) || (origin[ 0 ] > b->maxs[ 0 ] + sampleSize) ||
1278 origin[ 1 ] < (b->mins[ 1 ] - sampleSize) || (origin[ 1 ] > b->maxs[ 1 ] + sampleSize) ||
1279 origin[ 2 ] < (b->mins[ 2 ] - sampleSize) || (origin[ 2 ] > b->maxs[ 2 ] + sampleSize) )
1282 /* walk candidate luxels */
1283 VectorClear( average );
1286 for( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1288 for( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1290 /* ignore same luxels */
1291 if( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 )
1294 /* ignore unmapped/unlit luxels */
1295 cluster2 = SUPER_CLUSTER( x2, y2 );
1296 if( *cluster2 == CLUSTER_UNMAPPED )
1298 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1299 if( luxel2[ 3 ] <= 0.0f )
1302 /* get particulars */
1303 origin2 = SUPER_ORIGIN( x2, y2 );
1304 normal2 = SUPER_NORMAL( x2, y2 );
1307 if( DotProduct( normal, normal2 ) < 0.5f )
1311 if( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1312 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1313 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize )
1317 //% VectorSet( luxel2, 255, 0, 255 );
1318 luxels[ numLuxels++ ] = luxel2;
1319 VectorAdd( average, luxel2, average );
1320 totalColor += luxel2[ 3 ];
1325 if( numLuxels == 0 )
1329 ootc = 1.0f / totalColor;
1330 VectorScale( average, ootc, luxel );
1338 /* emit statistics */
1339 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
1340 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1347 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1350 #define SOLID_EPSILON 0.0625
1351 #define LUXEL_TOLERANCE 0.0025
1352 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1354 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1358 double delta, total, rd, gd, bd;
1359 float *aLuxel, *bLuxel;
1362 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1363 if( (minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ]) &&
1364 ((aNum == 0 && bNum != 0) || (aNum != 0 && bNum == 0)) )
1368 if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1369 a->brightness != b->brightness ||
1370 a->solid[ aNum ] != b->solid[ bNum ] ||
1371 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1374 /* compare solid color lightmaps */
1375 if( a->solid[ aNum ] && b->solid[ bNum ] )
1378 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1379 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1380 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1383 if( rd > SOLID_EPSILON || gd > SOLID_EPSILON|| bd > SOLID_EPSILON )
1390 /* compare nonsolid lightmaps */
1391 if( a->w != b->w || a->h != b->h )
1394 /* compare luxels */
1397 for( y = 0; y < a->h; y++ )
1399 for( x = 0; x < a->w; x++ )
1401 /* increment total */
1405 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1406 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1408 /* ignore unused luxels */
1409 if( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 )
1413 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1414 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1415 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1417 /* 2003-09-27: compare individual luxels */
1418 if( rd > 3.0 || gd > 3.0 || bd > 3.0 )
1421 /* compare (fixme: take into account perceptual differences) */
1422 delta += rd * LUXEL_COLOR_FRAC;
1423 delta += gd * LUXEL_COLOR_FRAC;
1424 delta += bd * LUXEL_COLOR_FRAC;
1426 /* is the change too high? */
1427 if( total > 0.0 && ((delta / total) > LUXEL_TOLERANCE) )
1432 /* made it this far, they must be identical (or close enough) */
1440 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1443 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1447 float luxel[ 3 ], *aLuxel, *bLuxel;
1451 if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1452 a->brightness != b->brightness ||
1453 a->solid[ aNum ] != b->solid[ bNum ] ||
1454 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1457 /* compare solid lightmaps */
1458 if( a->solid[ aNum ] && b->solid[ bNum ] )
1461 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1462 VectorScale( luxel, 0.5f, luxel );
1465 VectorCopy( luxel, a->solidColor[ aNum ] );
1466 VectorCopy( luxel, b->solidColor[ bNum ] );
1468 /* return to sender */
1472 /* compare nonsolid lightmaps */
1473 if( a->w != b->w || a->h != b->h )
1477 for( y = 0; y < a->h; y++ )
1479 for( x = 0; x < a->w; x++ )
1482 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1483 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1485 /* handle occlusion mismatch */
1486 if( aLuxel[ 0 ] < 0.0f )
1487 VectorCopy( bLuxel, aLuxel );
1488 else if( bLuxel[ 0 ] < 0.0f )
1489 VectorCopy( aLuxel, bLuxel );
1493 VectorAdd( aLuxel, bLuxel, luxel );
1494 VectorScale( luxel, 0.5f, luxel );
1496 /* debugging code */
1497 //% luxel[ 2 ] += 64.0f;
1500 VectorCopy( luxel, aLuxel );
1501 VectorCopy( luxel, bLuxel );
1514 determines if a single luxel is can be approximated with the interpolated vertex rgba
1517 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv )
1519 int i, x, y, d, lightmapNum;
1521 vec3_t color, vertexColor;
1522 byte cb[ 4 ], vcb[ 4 ];
1525 /* find luxel xy coords */
1526 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1527 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1530 else if( x >= lm->w )
1534 else if( y >= lm->h )
1538 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1541 if( lm->styles[ lightmapNum ] == LS_NONE )
1545 luxel = BSP_LUXEL( lightmapNum, x, y );
1547 /* ignore occluded luxels */
1548 if( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f )
1551 /* copy, set min color and compare */
1552 VectorCopy( luxel, color );
1553 VectorCopy( dv->color[ 0 ], vertexColor );
1555 /* styles are not affected by minlight */
1556 if( lightmapNum == 0 )
1558 for( i = 0; i < 3; i++ )
1561 if( color[ i ] < minLight[ i ] )
1562 color[ i ] = minLight[ i ];
1563 if( vertexColor[ i ] < minLight[ i ] ) /* note NOT minVertexLight */
1564 vertexColor[ i ] = minLight[ i ];
1569 ColorToBytes( color, cb, 1.0f );
1570 ColorToBytes( vertexColor, vcb, 1.0f );
1573 for( i = 0; i < 3; i++ )
1575 d = cb[ i ] - vcb[ i ];
1578 if( d > approximateTolerance )
1583 /* close enough for the girls i date */
1590 ApproximateTriangle()
1591 determines if a single triangle can be approximated with vertex rgba
1594 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] )
1596 bspDrawVert_t mid, *dv2[ 3 ];
1600 /* approximate the vertexes */
1601 if( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse )
1603 if( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse )
1605 if( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse )
1608 /* subdivide calc */
1611 float dx, dy, dist, maxDist;
1614 /* find the longest edge and split it */
1617 for( i = 0; i < 3; i++ )
1619 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 0 ];
1620 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 1 ];
1621 dist = sqrt( (dx * dx) + (dy * dy) );
1622 if( dist > maxDist )
1629 /* try to early out */
1630 if( i < 0 || maxDist < subdivideThreshold )
1634 /* split the longest edge and map it */
1635 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
1636 if( ApproximateLuxel( lm, &mid ) == qfalse )
1639 /* recurse to first triangle */
1640 VectorCopy( dv, dv2 );
1642 if( ApproximateTriangle_r( lm, dv2 ) == qfalse )
1645 /* recurse to second triangle */
1646 VectorCopy( dv, dv2 );
1647 dv2[ (max + 1) % 3 ] = ∣
1648 return ApproximateTriangle_r( lm, dv2 );
1654 ApproximateLightmap()
1655 determines if a raw lightmap can be approximated sufficiently with vertex colors
1658 static qboolean ApproximateLightmap( rawLightmap_t *lm )
1660 int n, num, i, x, y, pw[ 5 ], r;
1661 bspDrawSurface_t *ds;
1662 surfaceInfo_t *info;
1663 mesh_t src, *subdivided, *mesh;
1664 bspDrawVert_t *verts, *dv[ 3 ];
1665 qboolean approximated;
1668 /* approximating? */
1669 if( approximateTolerance <= 0 )
1672 /* test for jmonroe */
1674 /* don't approx lightmaps with styled twins */
1675 if( lm->numStyledTwins > 0 )
1678 /* don't approx lightmaps with styles */
1679 for( i = 1; i < MAX_LIGHTMAPS; i++ )
1681 if( lm->styles[ i ] != LS_NONE )
1686 /* assume reduced until shadow detail is found */
1687 approximated = qtrue;
1689 /* walk the list of surfaces on this raw lightmap */
1690 for( n = 0; n < lm->numLightSurfaces; n++ )
1693 num = lightSurfaces[ lm->firstLightSurface + n ];
1694 ds = &bspDrawSurfaces[ num ];
1695 info = &surfaceInfos[ num ];
1697 /* assume not-reduced initially */
1698 info->approximated = qfalse;
1700 /* bail if lightmap doesn't match up */
1701 if( info->lm != lm )
1704 /* bail if not vertex lit */
1705 if( info->si->noVertexLight )
1708 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1709 if( (info->maxs[ 0 ] - info->mins[ 0 ]) <= (2.0f * info->sampleSize) &&
1710 (info->maxs[ 1 ] - info->mins[ 1 ]) <= (2.0f * info->sampleSize) &&
1711 (info->maxs[ 2 ] - info->mins[ 2 ]) <= (2.0f * info->sampleSize) )
1713 info->approximated = qtrue;
1714 numSurfsVertexForced++;
1718 /* handle the triangles */
1719 switch( ds->surfaceType )
1723 verts = yDrawVerts + ds->firstVert;
1725 /* map the triangles */
1726 info->approximated = qtrue;
1727 for( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1729 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1730 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1731 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1732 info->approximated = ApproximateTriangle_r( lm, dv );
1737 /* make a mesh from the drawsurf */
1738 src.width = ds->patchWidth;
1739 src.height = ds->patchHeight;
1740 src.verts = &yDrawVerts[ ds->firstVert ];
1741 //% subdivided = SubdivideMesh( src, 8, 512 );
1742 subdivided = SubdivideMesh2( src, info->patchIterations );
1744 /* fit it to the curve and remove colinear verts on rows/columns */
1745 PutMeshOnCurve( *subdivided );
1746 mesh = RemoveLinearMeshColumnsRows( subdivided );
1747 FreeMesh( subdivided );
1750 verts = mesh->verts;
1752 /* map the mesh quads */
1753 info->approximated = qtrue;
1754 for( y = 0; y < (mesh->height - 1) && info->approximated; y++ )
1756 for( x = 0; x < (mesh->width - 1) && info->approximated; x++ )
1759 pw[ 0 ] = x + (y * mesh->width);
1760 pw[ 1 ] = x + ((y + 1) * mesh->width);
1761 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1762 pw[ 3 ] = x + 1 + (y * mesh->width);
1763 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1768 /* get drawverts and map first triangle */
1769 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1770 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1771 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1772 info->approximated = ApproximateTriangle_r( lm, dv );
1774 /* get drawverts and map second triangle */
1775 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1776 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1777 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1778 if( info->approximated )
1779 info->approximated = ApproximateTriangle_r( lm, dv );
1792 if( info->approximated == qfalse )
1793 approximated = qfalse;
1795 numSurfsVertexApproximated++;
1799 return approximated;
1805 TestOutLightmapStamp()
1806 tests a stamp on a given lightmap for validity
1809 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y )
1811 int sx, sy, ox, oy, offset;
1816 if( x < 0 || y < 0 || (x + lm->w) > olm->customWidth || (y + lm->h) > olm->customHeight )
1819 /* solid lightmaps test a 1x1 stamp */
1820 if( lm->solid[ lightmapNum ] )
1822 offset = (y * olm->customWidth) + x;
1823 if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1828 /* test the stamp */
1829 for( sy = 0; sy < lm->h; sy++ )
1831 for( sx = 0; sx < lm->w; sx++ )
1834 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1835 if( luxel[ 0 ] < 0.0f )
1838 /* get bsp lightmap coords and test */
1841 offset = (oy * olm->customWidth) + ox;
1842 if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1847 /* stamp is empty */
1855 sets up an output lightmap
1858 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm )
1861 if( lm == NULL || olm == NULL )
1864 /* is this a "normal" bsp-stored lightmap? */
1865 if( (lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize) || externalLightmaps )
1867 olm->lightmapNum = numBSPLightmaps;
1870 /* lightmaps are interleaved with light direction maps */
1875 olm->lightmapNum = -3;
1877 /* set external lightmap number */
1878 olm->extLightmapNum = -1;
1881 olm->numLightmaps = 0;
1882 olm->customWidth = lm->customWidth;
1883 olm->customHeight = lm->customHeight;
1884 olm->freeLuxels = olm->customWidth * olm->customHeight;
1885 olm->numShaders = 0;
1887 /* allocate buffers */
1888 olm->lightBits = safe_malloc( (olm->customWidth * olm->customHeight / 8) + 8 );
1889 memset( olm->lightBits, 0, (olm->customWidth * olm->customHeight / 8) + 8 );
1890 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1891 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1894 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1895 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1903 for a given surface lightmap, find output lightmap pages and positions for it
1906 static void FindOutLightmaps( rawLightmap_t *lm )
1908 int i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
1910 surfaceInfo_t *info;
1911 float *luxel, *deluxel;
1912 vec3_t color, direction;
1917 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1918 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1919 lm->outLightmapNums[ lightmapNum ] = -3;
1921 /* can this lightmap be approximated with vertex color? */
1922 if( ApproximateLightmap( lm ) )
1926 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1929 if( lm->styles[ lightmapNum ] == LS_NONE )
1932 /* don't store twinned lightmaps */
1933 if( lm->twins[ lightmapNum ] != NULL )
1936 /* if this is a styled lightmap, try some normalized locations first */
1938 if( lightmapNum > 0 && outLightmaps != NULL )
1941 for( j = 0; j < 2; j++ )
1943 /* try identical position */
1944 for( i = 0; i < numOutLightmaps; i++ )
1946 /* get the output lightmap */
1947 olm = &outLightmaps[ i ];
1949 /* simple early out test */
1950 if( olm->freeLuxels < lm->used )
1953 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
1954 if( olm->customWidth != lm->customWidth ||
1955 olm->customHeight != lm->customHeight )
1961 x = lm->lightmapX[ 0 ];
1962 y = lm->lightmapY[ 0 ];
1963 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1969 for( sy = -1; sy <= 1; sy++ )
1971 for( sx = -1; sx <= 1; sx++ )
1973 x = lm->lightmapX[ 0 ] + sx * (olm->customWidth >> 1); //% lm->w;
1974 y = lm->lightmapY[ 0 ] + sy * (olm->customHeight >> 1); //% lm->h;
1975 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1995 /* try normal placement algorithm */
2002 /* walk the list of lightmap pages */
2003 for( i = 0; i < numOutLightmaps; i++ )
2005 /* get the output lightmap */
2006 olm = &outLightmaps[ i ];
2008 /* simple early out test */
2009 if( olm->freeLuxels < lm->used )
2012 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2013 if( olm->customWidth != lm->customWidth ||
2014 olm->customHeight != lm->customHeight )
2018 if( lm->solid[ lightmapNum ] )
2020 xMax = olm->customWidth;
2021 yMax = olm->customHeight;
2025 xMax = (olm->customWidth - lm->w) + 1;
2026 yMax = (olm->customHeight - lm->h) + 1;
2029 /* walk the origin around the lightmap */
2030 for( y = 0; y < yMax; y++ )
2032 for( x = 0; x < xMax; x++ )
2034 /* find a fine tract of lauhnd */
2035 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2057 /* allocate two new output lightmaps */
2058 numOutLightmaps += 2;
2059 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2060 if( outLightmaps != NULL && numOutLightmaps > 2 )
2062 memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );
2063 free( outLightmaps );
2067 /* initialize both out lightmaps */
2068 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
2069 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
2071 /* set out lightmap */
2072 i = numOutLightmaps - 2;
2073 olm = &outLightmaps[ i ];
2075 /* set stamp xy origin to the first surface lightmap */
2076 if( lightmapNum > 0 )
2078 x = lm->lightmapX[ 0 ];
2079 y = lm->lightmapY[ 0 ];
2083 /* if this is a style-using lightmap, it must be exported */
2084 if( lightmapNum > 0 && game->load != LoadRBSPFile )
2085 olm->extLightmapNum = 0;
2087 /* add the surface lightmap to the bsp lightmap */
2088 lm->outLightmapNums[ lightmapNum ] = i;
2089 lm->lightmapX[ lightmapNum ] = x;
2090 lm->lightmapY[ lightmapNum ] = y;
2091 olm->numLightmaps++;
2094 for( i = 0; i < lm->numLightSurfaces; i++ )
2096 /* get surface info */
2097 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2099 /* test for shader */
2100 for( j = 0; j < olm->numShaders; j++ )
2102 if( olm->shaders[ j ] == info->si )
2106 /* if it doesn't exist, add it */
2107 if( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS )
2109 olm->shaders[ olm->numShaders ] = info->si;
2111 numLightmapShaders++;
2116 if( lm->solid[ lightmapNum ] )
2127 /* mark the bits used */
2128 for( y = 0; y < yMax; y++ )
2130 for( x = 0; x < xMax; x++ )
2133 luxel = BSP_LUXEL( lightmapNum, x, y );
2134 deluxel = BSP_DELUXEL( x, y );
2135 if( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ])
2138 /* set minimum light */
2139 if( lm->solid[ lightmapNum ] )
2142 VectorSet( color, 255.0f, 0.0f, 0.0f );
2144 VectorCopy( lm->solidColor[ lightmapNum ], color );
2147 VectorCopy( luxel, color );
2149 /* styles are not affected by minlight */
2150 if( lightmapNum == 0 )
2152 for( i = 0; i < 3; i++ )
2154 if( color[ i ] < minLight[ i ] )
2155 color[ i ] = minLight[ i ];
2159 /* get bsp lightmap coords */
2160 ox = x + lm->lightmapX[ lightmapNum ];
2161 oy = y + lm->lightmapY[ lightmapNum ];
2162 offset = (oy * olm->customWidth) + ox;
2164 /* flag pixel as used */
2165 olm->lightBits[ offset >> 3 ] |= (1 << (offset & 7));
2169 pixel = olm->bspLightBytes + (((oy * olm->customWidth) + ox) * 3);
2170 ColorToBytes( color, pixel, lm->brightness );
2172 /* store direction */
2175 if(normalizeDeluxemap)
2177 if(!VectorNormalize(deluxel, direction))
2178 VectorClear(direction);
2183 VectorScale(deluxel, 1 / deluxel[3], direction);
2185 VectorClear(direction);
2188 /* normalize average light direction */
2189 if(direction[0] != 0 || direction[1] != 0 || direction[2] != 0)
2191 /* encode [-1,1] in [0,255] */
2192 pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
2193 for( i = 0; i < 3; i++ )
2195 temp = (direction[ i ] + 1.0f) * 127.5f;
2198 else if( temp > 255 )
2213 CompareRawLightmap()
2214 compare function for qsort()
2217 static int CompareRawLightmap( const void *a, const void *b )
2219 rawLightmap_t *alm, *blm;
2220 surfaceInfo_t *aInfo, *bInfo;
2225 alm = &rawLightmaps[ *((int*) a) ];
2226 blm = &rawLightmaps[ *((int*) b) ];
2228 /* get min number of surfaces */
2229 min = (alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces);
2232 for( i = 0; i < min; i++ )
2234 /* get surface info */
2235 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2236 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2238 /* compare shader names */
2239 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2244 /* test style count */
2246 for( i = 0; i < MAX_LIGHTMAPS; i++ )
2247 diff += blm->styles[ i ] - alm->styles[ i ];
2252 diff = (blm->w * blm->h) - (alm->w * alm->h);
2256 /* must be equivalent */
2263 StoreSurfaceLightmaps()
2264 stores the surface lightmaps into the bsp as byte rgb triplets
2267 void StoreSurfaceLightmaps( void )
2269 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2270 int style, size, lightmapNum, lightmapNum2;
2271 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2272 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2273 float *deluxel, *bspDeluxel, *bspDeluxel2;
2275 int numUsed, numTwins, numTwinLuxels, numStored;
2276 float lmx, lmy, efficiency;
2278 bspDrawSurface_t *ds, *parent, dsTemp;
2279 surfaceInfo_t *info;
2280 rawLightmap_t *lm, *lm2;
2282 bspDrawVert_t *dv, *ydv, *dvParent;
2283 char dirname[ 1024 ], filename[ 1024 ];
2285 char lightmapName[ 128 ];
2286 char *rgbGenValues[ 256 ];
2287 char *alphaGenValues[ 256 ];
2291 Sys_Printf( "--- StoreSurfaceLightmaps ---\n");
2294 strcpy( dirname, source );
2295 StripExtension( dirname );
2296 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2297 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2299 /* -----------------------------------------------------------------
2300 average the sampled luxels into the bsp luxels
2301 ----------------------------------------------------------------- */
2304 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2306 /* walk the list of raw lightmaps */
2310 numSolidLightmaps = 0;
2311 for( i = 0; i < numRawLightmaps; i++ )
2314 lm = &rawLightmaps[ i ];
2316 /* walk individual lightmaps */
2317 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2320 if( lm->superLuxels[ lightmapNum ] == NULL )
2323 /* allocate bsp luxel storage */
2324 if( lm->bspLuxels[ lightmapNum ] == NULL )
2326 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2327 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2328 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2331 /* allocate radiosity lightmap storage */
2334 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2335 if( lm->radLuxels[ lightmapNum ] == NULL )
2336 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2337 memset( lm->radLuxels[ lightmapNum ], 0, size );
2340 /* average supersampled luxels */
2341 for( y = 0; y < lm->h; y++ )
2343 for( x = 0; x < lm->w; x++ )
2347 occludedSamples = 0.0f;
2349 VectorClear( sample );
2350 VectorClear( occludedSample );
2351 VectorClear( dirSample );
2352 for( ly = 0; ly < superSample; ly++ )
2354 for( lx = 0; lx < superSample; lx++ )
2357 sx = x * superSample + lx;
2358 sy = y * superSample + ly;
2359 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2360 deluxel = SUPER_DELUXEL( sx, sy );
2361 normal = SUPER_NORMAL( sx, sy );
2362 cluster = SUPER_CLUSTER( sx, sy );
2364 /* sample deluxemap */
2365 if( deluxemap && lightmapNum == 0 )
2366 VectorAdd( dirSample, deluxel, dirSample );
2368 /* keep track of used/occluded samples */
2369 if( *cluster != CLUSTER_UNMAPPED )
2372 /* handle lightmap border? */
2373 if( lightmapBorder && (sx == 0 || sx == (lm->sw - 1) || sy == 0 || sy == (lm->sh - 1) ) && luxel[ 3 ] > 0.0f )
2375 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2380 else if( debug && *cluster < 0 )
2382 if( *cluster == CLUSTER_UNMAPPED )
2383 VectorSet( luxel, 255, 204, 0 );
2384 else if( *cluster == CLUSTER_OCCLUDED )
2385 VectorSet( luxel, 255, 0, 255 );
2386 else if( *cluster == CLUSTER_FLOODED )
2387 VectorSet( luxel, 0, 32, 255 );
2388 VectorAdd( occludedSample, luxel, occludedSample );
2389 occludedSamples += 1.0f;
2392 /* normal luxel handling */
2393 else if( luxel[ 3 ] > 0.0f )
2395 /* handle lit or flooded luxels */
2396 if( *cluster > 0 || *cluster == CLUSTER_FLOODED )
2398 VectorAdd( sample, luxel, sample );
2399 samples += luxel[ 3 ];
2402 /* handle occluded or unmapped luxels */
2405 VectorAdd( occludedSample, luxel, occludedSample );
2406 occludedSamples += luxel[ 3 ];
2409 /* handle style debugging */
2410 if( debug && lightmapNum > 0 && x < 2 && y < 2 )
2412 VectorCopy( debugColors[ 0 ], sample );
2419 /* only use occluded samples if necessary */
2420 if( samples <= 0.0f )
2422 VectorCopy( occludedSample, sample );
2423 samples = occludedSamples;
2427 luxel = SUPER_LUXEL( lightmapNum, x, y );
2428 deluxel = SUPER_DELUXEL( x, y );
2430 /* store light direction */
2431 if( deluxemap && lightmapNum == 0 )
2432 VectorCopy( dirSample, deluxel );
2434 /* store the sample back in super luxels */
2435 if( samples > 0.01f )
2437 VectorScale( sample, (1.0f / samples), luxel );
2441 /* if any samples were mapped in any way, store ambient color */
2442 else if( mappedSamples > 0 )
2444 if( lightmapNum == 0 )
2445 VectorCopy( ambientColor, luxel );
2447 VectorClear( luxel );
2451 /* store a bogus value to be fixed later */
2454 VectorClear( luxel );
2462 ClearBounds( colorMins, colorMaxs );
2464 /* clean up and store into bsp luxels */
2465 for( y = 0; y < lm->h; y++ )
2467 for( x = 0; x < lm->w; x++ )
2470 luxel = SUPER_LUXEL( lightmapNum, x, y );
2471 deluxel = SUPER_DELUXEL( x, y );
2473 /* copy light direction */
2474 if( deluxemap && lightmapNum == 0 )
2475 VectorCopy( deluxel, dirSample );
2477 /* is this a valid sample? */
2478 if( luxel[ 3 ] > 0.0f )
2480 VectorCopy( luxel, sample );
2481 samples = luxel[ 3 ];
2485 /* fix negative samples */
2486 for( j = 0; j < 3; j++ )
2488 if( sample[ j ] < 0.0f )
2494 /* nick an average value from the neighbors */
2495 VectorClear( sample );
2496 VectorClear( dirSample );
2499 /* fixme: why is this disabled?? */
2500 for( sy = (y - 1); sy <= (y + 1); sy++ )
2502 if( sy < 0 || sy >= lm->h )
2505 for( sx = (x - 1); sx <= (x + 1); sx++ )
2507 if( sx < 0 || sx >= lm->w || (sx == x && sy == y) )
2510 /* get neighbor's particulars */
2511 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2512 if( luxel[ 3 ] < 0.0f )
2514 VectorAdd( sample, luxel, sample );
2515 samples += luxel[ 3 ];
2520 if( samples == 0.0f )
2522 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2530 /* fix negative samples */
2531 for( j = 0; j < 3; j++ )
2533 if( sample[ j ] < 0.0f )
2539 /* scale the sample */
2540 VectorScale( sample, (1.0f / samples), sample );
2542 /* store the sample in the radiosity luxels */
2545 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2546 VectorCopy( sample, radLuxel );
2548 /* if only storing bounced light, early out here */
2549 if( bounceOnly && !bouncing )
2553 /* store the sample in the bsp luxels */
2554 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2555 bspDeluxel = BSP_DELUXEL( x, y );
2557 VectorAdd( bspLuxel, sample, bspLuxel );
2558 if( deluxemap && lightmapNum == 0 )
2559 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2561 /* add color to bounds for solid checking */
2562 if( samples > 0.0f )
2563 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2567 /* set solid color */
2568 lm->solid[ lightmapNum ] = qfalse;
2569 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2570 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2572 /* nocollapse prevents solid lightmaps */
2573 if( noCollapse == qfalse )
2575 /* check solid color */
2576 VectorSubtract( colorMaxs, colorMins, sample );
2577 if( (sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON) ||
2578 (lm->w <= 2 && lm->h <= 2) ) /* small lightmaps get forced to solid color */
2581 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2582 lm->solid[ lightmapNum ] = qtrue;
2583 numSolidLightmaps++;
2586 /* if all lightmaps aren't solid, then none of them are solid */
2587 if( lm->solid[ lightmapNum ] != lm->solid[ 0 ] )
2589 for( y = 0; y < MAX_LIGHTMAPS; y++ )
2591 if( lm->solid[ y ] )
2592 numSolidLightmaps--;
2593 lm->solid[ y ] = qfalse;
2598 /* wrap bsp luxels if necessary */
2601 for( y = 0; y < lm->h; y++ )
2603 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2604 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2605 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2606 VectorScale( bspLuxel, 0.5f, bspLuxel );
2607 VectorCopy( bspLuxel, bspLuxel2 );
2608 if( deluxemap && lightmapNum == 0 )
2610 bspDeluxel = BSP_DELUXEL( 0, y );
2611 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2612 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2613 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2614 VectorCopy( bspDeluxel, bspDeluxel2 );
2620 for( x = 0; x < lm->w; x++ )
2622 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2623 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2624 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2625 VectorScale( bspLuxel, 0.5f, bspLuxel );
2626 VectorCopy( bspLuxel, bspLuxel2 );
2627 if( deluxemap && lightmapNum == 0 )
2629 bspDeluxel = BSP_DELUXEL( x, 0 );
2630 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2631 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2632 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2633 VectorCopy( bspDeluxel, bspDeluxel2 );
2640 /* -----------------------------------------------------------------
2641 collapse non-unique lightmaps
2642 ----------------------------------------------------------------- */
2644 if( noCollapse == qfalse && deluxemap == qfalse )
2647 Sys_FPrintf( SYS_VRB, "collapsing..." );
2649 /* set all twin refs to null */
2650 for( i = 0; i < numRawLightmaps; i++ )
2652 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2654 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2655 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2656 rawLightmaps[ i ].numStyledTwins = 0;
2660 /* walk the list of raw lightmaps */
2661 for( i = 0; i < numRawLightmaps; i++ )
2664 lm = &rawLightmaps[ i ];
2666 /* walk lightmaps */
2667 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2670 if( lm->bspLuxels[ lightmapNum ] == NULL ||
2671 lm->twins[ lightmapNum ] != NULL )
2674 /* find all lightmaps that are virtually identical to this one */
2675 for( j = i + 1; j < numRawLightmaps; j++ )
2678 lm2 = &rawLightmaps[ j ];
2680 /* walk lightmaps */
2681 for( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
2684 if( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
2685 lm2->twins[ lightmapNum2 ] != NULL )
2689 if( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2691 /* merge and set twin */
2692 if( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2694 lm2->twins[ lightmapNum2 ] = lm;
2695 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
2697 numTwinLuxels += (lm->w * lm->h);
2699 /* count styled twins */
2700 if( lightmapNum > 0 )
2701 lm->numStyledTwins++;
2710 /* -----------------------------------------------------------------
2711 sort raw lightmaps by shader
2712 ----------------------------------------------------------------- */
2715 Sys_FPrintf( SYS_VRB, "sorting..." );
2717 /* allocate a new sorted list */
2718 if( sortLightmaps == NULL )
2719 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
2721 /* fill it out and sort it */
2722 for( i = 0; i < numRawLightmaps; i++ )
2723 sortLightmaps[ i ] = i;
2724 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
2726 /* -----------------------------------------------------------------
2727 allocate output lightmaps
2728 ----------------------------------------------------------------- */
2731 Sys_FPrintf( SYS_VRB, "allocating..." );
2733 /* kill all existing output lightmaps */
2734 if( outLightmaps != NULL )
2736 for( i = 0; i < numOutLightmaps; i++ )
2738 free( outLightmaps[ i ].lightBits );
2739 free( outLightmaps[ i ].bspLightBytes );
2741 free( outLightmaps );
2742 outLightmaps = NULL;
2745 numLightmapShaders = 0;
2746 numOutLightmaps = 0;
2747 numBSPLightmaps = 0;
2748 numExtLightmaps = 0;
2750 /* find output lightmap */
2751 for( i = 0; i < numRawLightmaps; i++ )
2753 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2754 FindOutLightmaps( lm );
2757 /* set output numbers in twinned lightmaps */
2758 for( i = 0; i < numRawLightmaps; i++ )
2761 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2763 /* walk lightmaps */
2764 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2767 lm2 = lm->twins[ lightmapNum ];
2770 lightmapNum2 = lm->twinNums[ lightmapNum ];
2772 /* find output lightmap from twin */
2773 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
2774 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
2775 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
2779 /* -----------------------------------------------------------------
2780 store output lightmaps
2781 ----------------------------------------------------------------- */
2784 Sys_FPrintf( SYS_VRB, "storing..." );
2786 /* count the bsp lightmaps and allocate space */
2787 if( bspLightBytes != NULL )
2788 free( bspLightBytes );
2789 if( numBSPLightmaps == 0 || externalLightmaps )
2791 numBSPLightBytes = 0;
2792 bspLightBytes = NULL;
2796 numBSPLightBytes = (numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3);
2797 bspLightBytes = safe_malloc( numBSPLightBytes );
2798 memset( bspLightBytes, 0, numBSPLightBytes );
2801 /* walk the list of output lightmaps */
2802 for( i = 0; i < numOutLightmaps; i++ )
2804 /* get output lightmap */
2805 olm = &outLightmaps[ i ];
2807 /* is this a valid bsp lightmap? */
2808 if( olm->lightmapNum >= 0 && !externalLightmaps )
2810 /* copy lighting data */
2811 lb = bspLightBytes + (olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3);
2812 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
2814 /* copy direction data */
2817 lb = bspLightBytes + ((olm->lightmapNum + 1) * game->lightmapSize * game->lightmapSize * 3);
2818 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
2822 /* external lightmap? */
2823 if( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps )
2825 /* make a directory for the lightmaps */
2828 /* set external lightmap number */
2829 olm->extLightmapNum = numExtLightmaps;
2831 /* write lightmap */
2832 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2833 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2834 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
2837 /* write deluxemap */
2840 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2841 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2842 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
2845 if( debugDeluxemap )
2846 olm->extLightmapNum++;
2851 if( numExtLightmaps > 0 )
2852 Sys_FPrintf( SYS_VRB, "\n" );
2854 /* delete unused external lightmaps */
2855 for( i = numExtLightmaps; i; i++ )
2857 /* determine if file exists */
2858 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
2859 if( !FileExists( filename ) )
2866 /* -----------------------------------------------------------------
2867 project the lightmaps onto the bsp surfaces
2868 ----------------------------------------------------------------- */
2871 Sys_FPrintf( SYS_VRB, "projecting..." );
2873 /* walk the list of surfaces */
2874 for( i = 0; i < numBSPDrawSurfaces; i++ )
2876 /* get the surface and info */
2877 ds = &bspDrawSurfaces[ i ];
2878 info = &surfaceInfos[ i ];
2882 /* handle surfaces with identical parent */
2883 if( info->parentSurfaceNum >= 0 )
2885 /* preserve original data and get parent */
2886 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
2887 memcpy( &dsTemp, ds, sizeof( *ds ) );
2889 /* overwrite child with parent data */
2890 memcpy( ds, parent, sizeof( *ds ) );
2892 /* restore key parts */
2893 ds->fogNum = dsTemp.fogNum;
2894 ds->firstVert = dsTemp.firstVert;
2895 ds->firstIndex = dsTemp.firstIndex;
2896 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
2898 /* set vertex data */
2899 dv = &bspDrawVerts[ ds->firstVert ];
2900 dvParent = &bspDrawVerts[ parent->firstVert ];
2901 for( j = 0; j < ds->numVerts; j++ )
2903 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
2904 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
2911 /* handle vertex lit or approximated surfaces */
2912 else if( lm == NULL || lm->outLightmapNums[ 0 ] < 0 )
2914 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2916 ds->lightmapNum[ lightmapNum ] = -3;
2917 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
2921 /* handle lightmapped surfaces */
2924 /* walk lightmaps */
2925 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2928 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2930 /* handle unused style */
2931 if( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
2933 ds->lightmapNum[ lightmapNum ] = -3;
2937 /* get output lightmap */
2938 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
2940 /* set bsp lightmap number */
2941 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
2943 /* deluxemap debugging makes the deluxemap visible */
2944 if( deluxemap && debugDeluxemap && lightmapNum == 0 )
2945 ds->lightmapNum[ lightmapNum ]++;
2947 /* calc lightmap origin in texture space */
2948 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
2949 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
2951 /* calc lightmap st coords */
2952 dv = &bspDrawVerts[ ds->firstVert ];
2953 ydv = &yDrawVerts[ ds->firstVert ];
2954 for( j = 0; j < ds->numVerts; j++ )
2956 if( lm->solid[ lightmapNum ] )
2958 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (0.5f / (float) olm->customWidth);
2959 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (0.5f / (float) olm->customWidth);
2963 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (ydv[ j ].lightmap[ 0 ][ 0 ] / (superSample * olm->customWidth));
2964 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (ydv[ j ].lightmap[ 0 ][ 1 ] / (superSample * olm->customHeight));
2970 /* store vertex colors */
2971 dv = &bspDrawVerts[ ds->firstVert ];
2972 for( j = 0; j < ds->numVerts; j++ )
2974 /* walk lightmaps */
2975 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2977 /* handle unused style */
2978 if( ds->vertexStyles[ lightmapNum ] == LS_NONE )
2979 VectorClear( color );
2982 /* get vertex color */
2983 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
2984 VectorCopy( luxel, color );
2986 /* set minimum light */
2987 if( lightmapNum == 0 )
2989 for( k = 0; k < 3; k++ )
2990 if( color[ k ] < minVertexLight[ k ] )
2991 color[ k ] = minVertexLight[ k ];
2995 /* store to bytes */
2996 if( !info->si->noVertexLight )
2997 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3001 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3002 if( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) //% info->si->styleMarker > 0 )
3005 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3009 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3010 dv = &bspDrawVerts[ ds->firstVert ];
3012 /* depthFunc equal? */
3013 if( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED )
3018 /* generate stages for styled lightmaps */
3019 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3022 style = lm->styles[ lightmapNum ];
3023 if( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
3026 /* get output lightmap */
3027 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3030 if( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] )
3031 strcpy( lightmapName, "$lightmap" );
3033 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3035 /* get rgbgen string */
3036 if( rgbGenValues[ style ] == NULL )
3038 sprintf( key, "_style%drgbgen", style );
3039 rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3040 if( rgbGenValues[ style ][ 0 ] == '\0' )
3041 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3044 if( rgbGenValues[ style ][ 0 ] != '\0' )
3045 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3049 /* get alphagen string */
3050 if( alphaGenValues[ style ] == NULL )
3052 sprintf( key, "_style%dalphagen", style );
3053 alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3055 if( alphaGenValues[ style ][ 0 ] != '\0' )
3056 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3058 alphaGen[ 0 ] = '\0';
3060 /* calculate st offset */
3061 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3062 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3064 /* create additional stage */
3065 if( lmx == 0.0f && lmy == 0.0f )
3067 sprintf( styleStage, "\t{\n"
3068 "\t\tmap %s\n" /* lightmap */
3069 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3070 "%s" /* depthFunc equal */
3073 "\t\ttcGen lightmap\n"
3076 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
3082 sprintf( styleStage, "\t{\n"
3083 "\t\tmap %s\n" /* lightmap */
3084 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3085 "%s" /* depthFunc equal */
3088 "\t\ttcGen lightmap\n"
3089 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3092 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
3100 strcat( styleStages, styleStage );
3103 /* create custom shader */
3104 if( info->si->styleMarker == 2 )
3105 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3107 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3109 /* emit remap command */
3110 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3113 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3114 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3115 //% Sys_Printf( ")\n" );
3118 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3119 else if( olm != NULL && lm != NULL && !externalLightmaps &&
3120 (olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize) )
3122 /* get output lightmap */
3123 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3125 /* do some name mangling */
3126 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3128 /* create custom shader */
3129 csi = CustomShader( info->si, "$lightmap", lightmapName );
3132 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3133 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3134 //% Sys_Printf( ")\n" );
3137 /* use the normal plain-jane shader */
3139 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3143 Sys_FPrintf( SYS_VRB, "done.\n" );
3145 /* calc num stored */
3146 numStored = numBSPLightBytes / 3;
3147 efficiency = (numStored <= 0)
3149 : (float) numUsed / (float) numStored;
3152 Sys_Printf( "%9d luxels used\n", numUsed );
3153 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3154 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3155 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3156 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3157 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3158 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3159 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3160 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3162 /* write map shader file */
3163 WriteMapShaderFile();