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 ){
68 /* allocate a buffer and set it up */
69 buffer = safe_malloc( width * height * 3 + 18 );
70 memset( buffer, 0, 18 );
72 buffer[ 12 ] = width & 255;
73 buffer[ 13 ] = width >> 8;
74 buffer[ 14 ] = height & 255;
75 buffer[ 15 ] = height >> 8;
79 c = ( width * height * 3 ) + 18;
80 for ( i = 18; i < c; i += 3 )
82 buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
83 buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
84 buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
87 /* write it and free the buffer */
88 file = fopen( filename, "wb" );
90 Error( "Unable to open %s for writing", filename );
93 /* flip vertically? */
95 fwrite( buffer, 1, 18, file );
96 for ( in = buffer + ( ( height - 1 ) * width * 3 ) + 18; in >= buffer; in -= ( width * 3 ) )
97 fwrite( in, 1, ( width * 3 ), file );
100 fwrite( buffer, 1, c, file );
112 exports the lightmaps as a list of numbered tga images
115 void ExportLightmaps( void ){
117 char dirname[ 1024 ], filename[ 1024 ];
122 Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n" );
124 /* do some path mangling */
125 strcpy( dirname, source );
126 StripExtension( dirname );
129 if ( bspLightBytes == NULL ) {
130 Sys_Printf( "WARNING: No BSP lightmap data\n" );
134 /* make a directory for the lightmaps */
137 /* iterate through the lightmaps */
138 for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
140 /* write a tga image out */
141 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
142 Sys_Printf( "Writing %s\n", filename );
143 WriteTGA24( filename, lightmap, game->lightmapSize, game->lightmapSize, qfalse );
150 ExportLightmapsMain()
151 exports the lightmaps as a list of numbered tga images
154 int ExportLightmapsMain( int argc, char **argv ){
157 Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
161 /* do some path mangling */
162 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
163 StripExtension( source );
164 DefaultExtension( source, ".bsp" );
167 Sys_Printf( "Loading %s\n", source );
168 LoadBSPFile( source );
170 /* export the lightmaps */
173 /* return to sender */
180 ImportLightmapsMain()
181 imports the lightmaps from a list of numbered tga images
184 int ImportLightmapsMain( int argc, char **argv ){
185 int i, x, y, len, width, height;
186 char dirname[ 1024 ], filename[ 1024 ];
187 byte *lightmap, *buffer, *pixels, *in, *out;
192 Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
196 /* do some path mangling */
197 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
198 StripExtension( source );
199 DefaultExtension( source, ".bsp" );
202 Sys_Printf( "Loading %s\n", source );
203 LoadBSPFile( source );
206 Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n" );
208 /* do some path mangling */
209 strcpy( dirname, source );
210 StripExtension( dirname );
213 if ( bspLightBytes == NULL ) {
214 Error( "No lightmap data" );
217 /* make a directory for the lightmaps */
220 /* iterate through the lightmaps */
221 for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
223 /* read a tga image */
224 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
225 Sys_Printf( "Loading %s\n", filename );
227 len = vfsLoadFile( filename, (void*) &buffer, -1 );
229 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
233 /* parse file into an image */
235 LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
238 /* sanity check it */
239 if ( pixels == NULL ) {
240 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
243 if ( width != game->lightmapSize || height != game->lightmapSize ) {
244 Sys_Printf( "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
245 filename, width, height, game->lightmapSize, game->lightmapSize );
248 /* copy the pixels */
250 for ( y = 1; y <= game->lightmapSize; y++ )
252 out = lightmap + ( ( game->lightmapSize - y ) * game->lightmapSize * 3 );
253 for ( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
254 VectorCopy( in, out );
262 Sys_Printf( "writing %s\n", source );
263 WriteBSPFile( source );
265 /* return to sender */
271 /* -------------------------------------------------------------------------------
273 this section deals with projecting a lightmap onto a raw drawsurface
275 ------------------------------------------------------------------------------- */
278 CompareLightSurface()
279 compare function for qsort()
282 static int CompareLightSurface( const void *a, const void *b ){
283 shaderInfo_t *asi, *bsi;
287 asi = surfaceInfos[ *( (int*) a ) ].si;
288 bsi = surfaceInfos[ *( (int*) b ) ].si;
298 /* compare shader names */
299 return strcmp( asi->shader, bsi->shader );
306 allocates a raw lightmap's necessary buffers
309 void FinishRawLightmap( rawLightmap_t *lm ){
310 int i, j, c, size, *sc;
315 /* sort light surfaces by shader name */
316 qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
319 lm->numLightClusters = 0;
320 for ( i = 0; i < lm->numLightSurfaces; i++ )
322 /* get surface info */
323 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
325 /* add surface clusters */
326 lm->numLightClusters += info->numSurfaceClusters;
329 /* allocate buffer for clusters and copy */
330 lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
332 for ( i = 0; i < lm->numLightSurfaces; i++ )
334 /* get surface info */
335 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
337 /* add surface clusters */
338 for ( j = 0; j < info->numSurfaceClusters; j++ )
339 lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
343 lm->styles[ 0 ] = LS_NORMAL;
344 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
345 lm->styles[ i ] = LS_NONE;
347 /* set supersampling size */
348 lm->sw = lm->w * superSample;
349 lm->sh = lm->h * superSample;
351 /* add to super luxel count */
352 numRawSuperLuxels += ( lm->sw * lm->sh );
354 /* manipulate origin/vecs for supersampling */
355 if ( superSample > 1 && lm->vecs != NULL ) {
356 /* calc inverse supersample */
357 is = 1.0f / superSample;
359 /* scale the vectors and shift the origin */
361 /* new code that works for arbitrary supersampling values */
362 VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
363 VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
364 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
365 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
366 VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
367 VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
369 /* old code that only worked with a value of 2 */
370 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
371 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
372 VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
373 VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
377 /* allocate bsp lightmap storage */
378 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
379 if ( lm->bspLuxels[ 0 ] == NULL ) {
380 lm->bspLuxels[ 0 ] = safe_malloc( size );
382 memset( lm->bspLuxels[ 0 ], 0, size );
384 /* allocate radiosity lightmap storage */
386 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
387 if ( lm->radLuxels[ 0 ] == NULL ) {
388 lm->radLuxels[ 0 ] = safe_malloc( size );
390 memset( lm->radLuxels[ 0 ], 0, size );
393 /* allocate sampling lightmap storage */
394 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
395 if ( lm->superLuxels[ 0 ] == NULL ) {
396 lm->superLuxels[ 0 ] = safe_malloc( size );
398 memset( lm->superLuxels[ 0 ], 0, size );
400 /* allocate origin map storage */
401 size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
402 if ( lm->superOrigins == NULL ) {
403 lm->superOrigins = safe_malloc( size );
405 memset( lm->superOrigins, 0, size );
407 /* allocate normal map storage */
408 size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
409 if ( lm->superNormals == NULL ) {
410 lm->superNormals = safe_malloc( size );
412 memset( lm->superNormals, 0, size );
414 /* allocate cluster map storage */
415 size = lm->sw * lm->sh * sizeof( int );
416 if ( lm->superClusters == NULL ) {
417 lm->superClusters = safe_malloc( size );
419 size = lm->sw * lm->sh;
420 sc = lm->superClusters;
421 for ( i = 0; i < size; i++ )
422 ( *sc++ ) = CLUSTER_UNMAPPED;
424 /* deluxemap allocation */
426 /* allocate sampling deluxel storage */
427 size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
428 if ( lm->superDeluxels == NULL ) {
429 lm->superDeluxels = safe_malloc( size );
431 memset( lm->superDeluxels, 0, size );
433 /* allocate bsp deluxel storage */
434 size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
435 if ( lm->bspDeluxels == NULL ) {
436 lm->bspDeluxels = safe_malloc( size );
438 memset( lm->bspDeluxels, 0, size );
442 numLuxels += ( lm->sw * lm->sh );
448 AddPatchToRawLightmap()
449 projects a lightmap for a patch surface
450 since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
451 it is no longer necessary for patch verts to fall exactly on a lightmap sample
452 based on AllocateLightmapForPatch()
455 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm ){
456 bspDrawSurface_t *ds;
459 bspDrawVert_t *verts, *a, *b;
461 mesh_t src, *subdivided, *mesh;
462 float sBasis, tBasis, s, t;
463 float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
466 /* patches finish a raw lightmap */
467 lm->finished = qtrue;
469 /* get surface and info */
470 ds = &bspDrawSurfaces[ num ];
471 info = &surfaceInfos[ num ];
473 /* make a temporary mesh from the drawsurf */
474 src.width = ds->patchWidth;
475 src.height = ds->patchHeight;
476 src.verts = &yDrawVerts[ ds->firstVert ];
477 //% subdivided = SubdivideMesh( src, 8, 512 );
478 subdivided = SubdivideMesh2( src, info->patchIterations );
480 /* fit it to the curve and remove colinear verts on rows/columns */
481 PutMeshOnCurve( *subdivided );
482 mesh = RemoveLinearMeshColumnsRows( subdivided );
483 FreeMesh( subdivided );
485 /* find the longest distance on each row/column */
487 memset( widthTable, 0, sizeof( widthTable ) );
488 memset( heightTable, 0, sizeof( heightTable ) );
489 for ( y = 0; y < mesh->height; y++ )
491 for ( x = 0; x < mesh->width; x++ )
494 if ( x + 1 < mesh->width ) {
495 a = &verts[ ( y * mesh->width ) + x ];
496 b = &verts[ ( y * mesh->width ) + x + 1 ];
497 VectorSubtract( a->xyz, b->xyz, delta );
498 length = VectorLength( delta );
499 if ( length > widthTable[ x ] ) {
500 widthTable[ x ] = length;
505 if ( y + 1 < mesh->height ) {
506 a = &verts[ ( y * mesh->width ) + x ];
507 b = &verts[ ( ( y + 1 ) * mesh->width ) + x ];
508 VectorSubtract( a->xyz, b->xyz, delta );
509 length = VectorLength( delta );
510 if ( length > heightTable[ y ] ) {
511 heightTable[ y ] = length;
517 /* determine lightmap width */
519 for ( x = 0; x < ( mesh->width - 1 ); x++ )
520 length += widthTable[ x ];
521 lm->w = ceil( length / lm->sampleSize ) + 1;
522 if ( lm->w < ds->patchWidth ) {
523 lm->w = ds->patchWidth;
525 if ( lm->w > lm->customWidth ) {
526 lm->w = lm->customWidth;
528 sBasis = (float) ( lm->w - 1 ) / (float) ( ds->patchWidth - 1 );
530 /* determine lightmap height */
532 for ( y = 0; y < ( mesh->height - 1 ); y++ )
533 length += heightTable[ y ];
534 lm->h = ceil( length / lm->sampleSize ) + 1;
535 if ( lm->h < ds->patchHeight ) {
536 lm->h = ds->patchHeight;
538 if ( lm->h > lm->customHeight ) {
539 lm->h = lm->customHeight;
541 tBasis = (float) ( lm->h - 1 ) / (float) ( ds->patchHeight - 1 );
543 /* free the temporary mesh */
546 /* set the lightmap texture coordinates in yDrawVerts */
547 lm->wrap[ 0 ] = qtrue;
548 lm->wrap[ 1 ] = qtrue;
549 verts = &yDrawVerts[ ds->firstVert ];
550 for ( y = 0; y < ds->patchHeight; y++ )
552 t = ( tBasis * y ) + 0.5f;
553 for ( x = 0; x < ds->patchWidth; x++ )
555 s = ( sBasis * x ) + 0.5f;
556 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
557 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
559 if ( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ( ( ds->patchHeight - 1 ) * ds->patchWidth ) + x ].xyz ) ) {
560 lm->wrap[ 1 ] = qfalse;
564 if ( !VectorCompare( verts[ ( y * ds->patchWidth ) ].xyz, verts[ ( y * ds->patchWidth ) + ( ds->patchWidth - 1 ) ].xyz ) ) {
565 lm->wrap[ 0 ] = qfalse;
570 //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
571 //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
572 //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
573 //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
574 //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
577 numPatchesLightmapped++;
586 AddSurfaceToRawLightmap()
587 projects a lightmap for a surface
588 based on AllocateLightmapForSurface()
591 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm ){
592 bspDrawSurface_t *ds, *ds2;
593 surfaceInfo_t *info, *info2;
594 int num2, n, i, axisNum;
595 float s, t, d, len, sampleSize;
596 vec3_t mins, maxs, origin, faxis, size, exactSize, delta, normalized, vecs[ 2 ];
598 bspDrawVert_t *verts;
601 /* get surface and info */
602 ds = &bspDrawSurfaces[ num ];
603 info = &surfaceInfos[ num ];
605 /* add the surface to the raw lightmap */
606 lightSurfaces[ numLightSurfaces++ ] = num;
607 lm->numLightSurfaces++;
609 /* does this raw lightmap already have any surfaces? */
610 if ( lm->numLightSurfaces > 1 ) {
611 /* surface and raw lightmap must have the same lightmap projection axis */
612 if ( VectorCompare( info->axis, lm->axis ) == qfalse ) {
616 /* match identical attributes */
617 if ( info->sampleSize != lm->sampleSize ||
618 info->entityNum != lm->entityNum ||
619 info->recvShadows != lm->recvShadows ||
620 info->si->lmCustomWidth != lm->customWidth ||
621 info->si->lmCustomHeight != lm->customHeight ||
622 info->si->lmBrightness != lm->brightness ||
623 info->si->lmFilterRadius != lm->filterRadius ||
624 info->si->splotchFix != lm->splotchFix ) {
628 /* surface bounds must intersect with raw lightmap bounds */
629 for ( i = 0; i < 3; i++ )
631 if ( info->mins[ i ] > lm->maxs[ i ] ) {
634 if ( info->maxs[ i ] < lm->mins[ i ] ) {
639 /* plane check (fixme: allow merging of nonplanars) */
640 if ( info->si->lmMergable == qfalse ) {
641 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 ) {
652 /* debug code hacking */
653 //% if( lm->numLightSurfaces > 1 )
658 if ( info->plane == NULL ) {
662 /* add surface to lightmap bounds */
663 AddPointToBounds( info->mins, lm->mins, lm->maxs );
664 AddPointToBounds( info->maxs, lm->mins, lm->maxs );
666 /* check to see if this is a non-planar patch */
667 if ( ds->surfaceType == MST_PATCH &&
668 lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f ) {
669 return AddPatchToRawLightmap( num, lm );
672 /* start with initially requested sample size */
673 sampleSize = lm->sampleSize;
675 /* round to the lightmap resolution */
676 for ( i = 0; i < 3; i++ )
678 exactSize[ i ] = lm->maxs[ i ] - lm->mins[ i ];
679 mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
680 maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
681 size[ i ] = ( maxs[ i ] - mins[ i ] ) / sampleSize + 1.0f;
683 /* hack (god this sucks) */
684 if ( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight ) {
690 /* set actual sample size */
691 lm->actualSampleSize = sampleSize;
693 /* fixme: copy rounded mins/maxes to lightmap record? */
694 if ( lm->plane == NULL ) {
695 VectorCopy( mins, lm->mins );
696 VectorCopy( maxs, lm->maxs );
697 VectorCopy( mins, origin );
700 /* set lightmap origin */
701 VectorCopy( lm->mins, origin );
703 /* make absolute axis */
704 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
705 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
706 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
708 /* clear out lightmap vectors */
709 memset( vecs, 0, sizeof( vecs ) );
711 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
712 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 ] ) {
723 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
724 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
731 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
732 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
735 /* check for bogus axis */
736 if ( faxis[ axisNum ] == 0.0f ) {
737 Sys_Printf( "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
742 /* store the axis number in the lightmap */
743 lm->axisNum = axisNum;
745 /* walk the list of surfaces on this raw lightmap */
746 for ( n = 0; n < lm->numLightSurfaces; n++ )
749 num2 = lightSurfaces[ lm->firstLightSurface + n ];
750 ds2 = &bspDrawSurfaces[ num2 ];
751 info2 = &surfaceInfos[ num2 ];
752 verts = &yDrawVerts[ ds2->firstVert ];
754 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
755 for ( i = 0; i < ds2->numVerts; i++ )
757 VectorSubtract( verts[ i ].xyz, origin, delta );
758 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
759 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
760 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
761 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
763 if ( s > (float) lm->w || t > (float) lm->h ) {
764 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
765 s, lm->w, t, lm->h );
770 /* get first drawsurface */
771 num2 = lightSurfaces[ lm->firstLightSurface ];
772 ds2 = &bspDrawSurfaces[ num2 ];
773 info2 = &surfaceInfos[ num2 ];
774 verts = &yDrawVerts[ ds2->firstVert ];
776 /* calculate lightmap origin */
777 if ( VectorLength( ds2->lightmapVecs[ 2 ] ) ) {
778 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
781 VectorCopy( lm->axis, plane );
783 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
785 VectorCopy( origin, lm->origin );
786 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
787 d /= plane[ axisNum ];
788 lm->origin[ axisNum ] -= d;
791 VectorCopy( lm->origin, ds->lightmapOrigin );
793 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
794 if ( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) { /* ydnar: can't remember what exactly i was thinking here... */
795 /* allocate space for the vectors */
796 lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
797 memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
798 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
800 /* project stepped lightmap blocks and subtract to get planevecs */
801 for ( i = 0; i < 2; i++ )
803 len = VectorNormalize( vecs[ i ], normalized );
804 VectorScale( normalized, ( 1.0 / len ), lm->vecs[ i ] );
805 d = DotProduct( lm->vecs[ i ], plane );
806 d /= plane[ axisNum ];
807 lm->vecs[ i ][ axisNum ] -= d;
812 /* lightmap vectors are useless on a non-planar surface */
817 if ( ds->surfaceType == MST_PATCH ) {
818 numPatchesLightmapped++;
819 if ( lm->plane != NULL ) {
820 numPlanarPatchesLightmapped++;
825 if ( lm->plane != NULL ) {
826 numPlanarsLightmapped++;
829 numNonPlanarsLightmapped++;
841 compare function for qsort()
844 static int CompareSurfaceInfo( const void *a, const void *b ){
845 surfaceInfo_t *aInfo, *bInfo;
849 /* get surface info */
850 aInfo = &surfaceInfos[ *( (int*) a ) ];
851 bInfo = &surfaceInfos[ *( (int*) b ) ];
854 if ( aInfo->model < bInfo->model ) {
857 else if ( aInfo->model > bInfo->model ) {
861 /* then lightmap status */
862 if ( aInfo->hasLightmap < bInfo->hasLightmap ) {
865 else if ( aInfo->hasLightmap > bInfo->hasLightmap ) {
869 /* then lightmap sample size */
870 if ( aInfo->sampleSize < bInfo->sampleSize ) {
873 else if ( aInfo->sampleSize > bInfo->sampleSize ) {
877 /* then lightmap axis */
878 for ( i = 0; i < 3; i++ )
880 if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
883 else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
889 if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
892 else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
895 else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
896 for ( i = 0; i < 4; i++ )
898 if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
901 else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
907 /* then position in world */
908 for ( i = 0; i < 3; i++ )
910 if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
913 else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
918 /* these are functionally identical (this should almost never happen) */
925 SetupSurfaceLightmaps()
926 allocates lightmaps for every surface in the bsp that needs one
927 this depends on yDrawVerts being allocated
930 void SetupSurfaceLightmaps( void ){
931 int i, j, k, s,num, num2;
934 bspDrawSurface_t *ds, *ds2;
935 surfaceInfo_t *info, *info2;
938 vec3_t mapSize, entityOrigin;
942 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
944 /* determine supersample amount */
945 if ( superSample < 1 ) {
948 else if ( superSample > 8 ) {
949 Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
953 /* clear map bounds */
954 ClearBounds( mapMins, mapMaxs );
956 /* allocate a list of surface clusters */
957 numSurfaceClusters = 0;
958 maxSurfaceClusters = numBSPLeafSurfaces;
959 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
960 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
962 /* allocate a list for per-surface info */
963 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
964 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
965 for ( i = 0; i < numBSPDrawSurfaces; i++ )
966 surfaceInfos[ i ].childSurfaceNum = -1;
968 /* allocate a list of surface indexes to be sorted */
969 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
970 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
972 /* walk each model in the bsp */
973 for ( i = 0; i < numBSPModels; i++ )
976 model = &bspModels[ i ];
978 /* walk the list of surfaces in this model and fill out the info structs */
979 for ( j = 0; j < model->numBSPSurfaces; j++ )
981 /* make surface index */
982 num = model->firstBSPSurface + j;
984 /* copy index to sort list */
985 sortSurfaces[ num ] = num;
987 /* get surface and info */
988 ds = &bspDrawSurfaces[ num ];
989 info = &surfaceInfos[ num ];
991 /* set entity origin */
992 if ( ds->numVerts > 0 ) {
993 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
996 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 );
1010 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1011 info->entityNum = GetSurfaceExtraEntityNum( num );
1012 info->castShadows = GetSurfaceExtraCastShadows( num );
1013 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1014 info->sampleSize = GetSurfaceExtraSampleSize( num );
1015 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1016 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1017 GetSurfaceExtraLightmapAxis( num, info->axis );
1020 if ( info->parentSurfaceNum >= 0 ) {
1021 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1024 /* determine surface bounds */
1025 ClearBounds( info->mins, info->maxs );
1026 for ( k = 0; k < ds->numVerts; k++ )
1028 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1029 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1032 /* find all the bsp clusters the surface falls into */
1033 for ( k = 0; k < numBSPLeafs; k++ )
1036 leaf = &bspLeafs[ k ];
1039 if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1040 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1041 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
1045 /* test leaf surfaces */
1046 for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1048 if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
1049 if ( numSurfaceClusters >= maxSurfaceClusters ) {
1050 Error( "maxSurfaceClusters exceeded" );
1052 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1053 numSurfaceClusters++;
1054 info->numSurfaceClusters++;
1059 /* determine if surface is planar */
1060 if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
1062 info->plane = safe_malloc( 4 * sizeof( float ) );
1063 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1064 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1067 /* determine if surface requires a lightmap */
1068 if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
1069 ds->surfaceType == MST_FOLIAGE ||
1070 ( info->si->compileFlags & C_VERTEXLIT ) ) {
1071 numSurfsVertexLit++;
1075 numSurfsLightmapped++;
1076 info->hasLightmap = qtrue;
1081 /* find longest map distance */
1082 VectorSubtract( mapMaxs, mapMins, mapSize );
1083 maxMapDistance = VectorLength( mapSize );
1085 /* sort the surfaces info list */
1086 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1088 /* allocate a list of surfaces that would go into raw lightmaps */
1089 numLightSurfaces = 0;
1090 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1091 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1093 /* allocate a list of raw lightmaps */
1094 numRawSuperLuxels = 0;
1095 numRawLightmaps = 0;
1096 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1097 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1099 /* walk the list of sorted surfaces */
1100 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1102 /* get info and attempt early out */
1103 num = sortSurfaces[ i ];
1104 ds = &bspDrawSurfaces[ num ];
1105 info = &surfaceInfos[ num ];
1106 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1110 /* allocate a new raw lightmap */
1111 lm = &rawLightmaps[ numRawLightmaps ];
1115 lm->splotchFix = info->si->splotchFix;
1116 lm->firstLightSurface = numLightSurfaces;
1117 lm->numLightSurfaces = 0;
1118 lm->sampleSize = info->sampleSize;
1119 lm->actualSampleSize = info->sampleSize;
1120 lm->entityNum = info->entityNum;
1121 lm->recvShadows = info->recvShadows;
1122 lm->brightness = info->si->lmBrightness;
1123 lm->filterRadius = info->si->lmFilterRadius;
1124 VectorCopy( info->axis, lm->axis );
1125 lm->plane = info->plane;
1126 VectorCopy( info->mins, lm->mins );
1127 VectorCopy( info->maxs, lm->maxs );
1129 lm->customWidth = info->si->lmCustomWidth;
1130 lm->customHeight = info->si->lmCustomHeight;
1132 /* add the surface to the raw lightmap */
1133 AddSurfaceToRawLightmap( num, lm );
1136 /* do an exhaustive merge */
1140 /* walk the list of surfaces again */
1142 for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1144 /* get info and attempt early out */
1145 num2 = sortSurfaces[ j ];
1146 ds2 = &bspDrawSurfaces[ num2 ];
1147 info2 = &surfaceInfos[ num2 ];
1148 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1152 /* add the surface to the raw lightmap */
1153 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1160 lm->numLightSurfaces--;
1166 /* finish the lightmap and allocate the various buffers */
1167 FinishRawLightmap( lm );
1170 /* allocate vertex luxel storage */
1171 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1173 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1174 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1175 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1176 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1179 /* emit some stats */
1180 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1181 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1182 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1183 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1184 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1185 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1186 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1187 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1193 StitchSurfaceLightmaps()
1194 stitches lightmap edges
1195 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1198 #define MAX_STITCH_CANDIDATES 32
1199 #define MAX_STITCH_LUXELS 64
1201 void StitchSurfaceLightmaps( void ){
1202 int i, j, x, y, x2, y2, *cluster, *cluster2,
1203 numStitched, numCandidates, numLuxels, f, fOld, start;
1204 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1205 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1206 sampleSize, average[ 3 ], totalColor, ootc, *luxels[ MAX_STITCH_LUXELS ];
1209 /* disabled for now */
1213 Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1217 start = I_FloatTime();
1219 /* walk the list of raw lightmaps */
1221 for ( i = 0; i < numRawLightmaps; i++ )
1223 /* print pacifier */
1224 f = 10 * i / numRawLightmaps;
1227 Sys_Printf( "%i...", f );
1230 /* get lightmap a */
1231 a = &rawLightmaps[ i ];
1233 /* walk rest of lightmaps */
1235 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1237 /* get lightmap b */
1238 b = &rawLightmaps[ j ];
1240 /* test bounding box */
1241 if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1242 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1243 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1248 c[ numCandidates++ ] = b;
1252 for ( y = 0; y < a->sh; y++ )
1254 for ( x = 0; x < a->sw; x++ )
1256 /* ignore unmapped/unlit luxels */
1258 cluster = SUPER_CLUSTER( x, y );
1259 if ( *cluster == CLUSTER_UNMAPPED ) {
1262 luxel = SUPER_LUXEL( 0, x, y );
1263 if ( luxel[ 3 ] <= 0.0f ) {
1267 /* get particulars */
1268 origin = SUPER_ORIGIN( x, y );
1269 normal = SUPER_NORMAL( x, y );
1271 /* walk candidate list */
1272 for ( j = 0; j < numCandidates; j++ )
1278 /* set samplesize to the smaller of the pair */
1279 sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1281 /* test bounding box */
1282 if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1283 origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1284 origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1288 /* walk candidate luxels */
1289 VectorClear( average );
1292 for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1294 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1296 /* ignore same luxels */
1297 if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1301 /* ignore unmapped/unlit luxels */
1302 cluster2 = SUPER_CLUSTER( x2, y2 );
1303 if ( *cluster2 == CLUSTER_UNMAPPED ) {
1306 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1307 if ( luxel2[ 3 ] <= 0.0f ) {
1311 /* get particulars */
1312 origin2 = SUPER_ORIGIN( x2, y2 );
1313 normal2 = SUPER_NORMAL( x2, y2 );
1316 if ( DotProduct( normal, normal2 ) < 0.5f ) {
1321 if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1322 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1323 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1328 //% VectorSet( luxel2, 255, 0, 255 );
1329 luxels[ numLuxels++ ] = luxel2;
1330 VectorAdd( average, luxel2, average );
1331 totalColor += luxel2[ 3 ];
1336 if ( numLuxels == 0 ) {
1341 ootc = 1.0f / totalColor;
1342 VectorScale( average, ootc, luxel );
1350 /* emit statistics */
1351 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1352 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1359 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1362 #define SOLID_EPSILON 0.0625
1363 #define LUXEL_TOLERANCE 0.0025
1364 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1366 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1369 double delta, total, rd, gd, bd;
1370 float *aLuxel, *bLuxel;
1373 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1374 if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1375 ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1380 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1381 a->brightness != b->brightness ||
1382 a->solid[ aNum ] != b->solid[ bNum ] ||
1383 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1387 /* compare solid color lightmaps */
1388 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1390 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1391 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1392 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1395 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1403 /* compare nonsolid lightmaps */
1404 if ( a->w != b->w || a->h != b->h ) {
1408 /* compare luxels */
1411 for ( y = 0; y < a->h; y++ )
1413 for ( x = 0; x < a->w; x++ )
1415 /* increment total */
1419 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1420 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1422 /* ignore unused luxels */
1423 if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1428 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1429 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1430 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1432 /* 2003-09-27: compare individual luxels */
1433 if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1437 /* compare (fixme: take into account perceptual differences) */
1438 delta += rd * LUXEL_COLOR_FRAC;
1439 delta += gd * LUXEL_COLOR_FRAC;
1440 delta += bd * LUXEL_COLOR_FRAC;
1442 /* is the change too high? */
1443 if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1449 /* made it this far, they must be identical (or close enough) */
1457 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1460 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1463 float luxel[ 3 ], *aLuxel, *bLuxel;
1467 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1468 a->brightness != b->brightness ||
1469 a->solid[ aNum ] != b->solid[ bNum ] ||
1470 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1474 /* compare solid lightmaps */
1475 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1477 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1478 VectorScale( luxel, 0.5f, luxel );
1481 VectorCopy( luxel, a->solidColor[ aNum ] );
1482 VectorCopy( luxel, b->solidColor[ bNum ] );
1484 /* return to sender */
1488 /* compare nonsolid lightmaps */
1489 if ( a->w != b->w || a->h != b->h ) {
1494 for ( y = 0; y < a->h; y++ )
1496 for ( x = 0; x < a->w; x++ )
1499 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1500 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1502 /* handle occlusion mismatch */
1503 if ( aLuxel[ 0 ] < 0.0f ) {
1504 VectorCopy( bLuxel, aLuxel );
1506 else if ( bLuxel[ 0 ] < 0.0f ) {
1507 VectorCopy( aLuxel, bLuxel );
1512 VectorAdd( aLuxel, bLuxel, luxel );
1513 VectorScale( luxel, 0.5f, luxel );
1515 /* debugging code */
1516 //% luxel[ 2 ] += 64.0f;
1519 VectorCopy( luxel, aLuxel );
1520 VectorCopy( luxel, bLuxel );
1533 determines if a single luxel is can be approximated with the interpolated vertex rgba
1536 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1537 int i, x, y, d, lightmapNum;
1539 vec3_t color, vertexColor;
1540 byte cb[ 4 ], vcb[ 4 ];
1543 /* find luxel xy coords */
1544 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1545 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1549 else if ( x >= lm->w ) {
1555 else if ( y >= lm->h ) {
1560 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1563 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1568 luxel = BSP_LUXEL( lightmapNum, x, y );
1570 /* ignore occluded luxels */
1571 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1575 /* copy, set min color and compare */
1576 VectorCopy( luxel, color );
1577 VectorCopy( dv->color[ 0 ], vertexColor );
1579 /* styles are not affected by minlight */
1580 if ( lightmapNum == 0 ) {
1581 for ( i = 0; i < 3; i++ )
1584 if ( color[ i ] < minLight[ i ] ) {
1585 color[ i ] = minLight[ i ];
1587 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1588 vertexColor[ i ] = minLight[ i ];
1594 ColorToBytes( color, cb, 1.0f );
1595 ColorToBytes( vertexColor, vcb, 1.0f );
1598 for ( i = 0; i < 3; i++ )
1600 d = cb[ i ] - vcb[ i ];
1604 if ( d > approximateTolerance ) {
1610 /* close enough for the girls i date */
1617 ApproximateTriangle()
1618 determines if a single triangle can be approximated with vertex rgba
1621 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1622 bspDrawVert_t mid, *dv2[ 3 ];
1626 /* approximate the vertexes */
1627 if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1630 if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1633 if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1637 /* subdivide calc */
1640 float dx, dy, dist, maxDist;
1643 /* find the longest edge and split it */
1646 for ( i = 0; i < 3; i++ )
1648 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1649 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1650 dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1651 if ( dist > maxDist ) {
1657 /* try to early out */
1658 if ( i < 0 || maxDist < subdivideThreshold ) {
1663 /* split the longest edge and map it */
1664 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1665 if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1669 /* recurse to first triangle */
1670 VectorCopy( dv, dv2 );
1672 if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1676 /* recurse to second triangle */
1677 VectorCopy( dv, dv2 );
1678 dv2[ ( max + 1 ) % 3 ] = ∣
1679 return ApproximateTriangle_r( lm, dv2 );
1685 ApproximateLightmap()
1686 determines if a raw lightmap can be approximated sufficiently with vertex colors
1689 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1690 int n, num, i, x, y, pw[ 5 ], r;
1691 bspDrawSurface_t *ds;
1692 surfaceInfo_t *info;
1693 mesh_t src, *subdivided, *mesh;
1694 bspDrawVert_t *verts, *dv[ 3 ];
1695 qboolean approximated;
1698 /* approximating? */
1699 if ( approximateTolerance <= 0 ) {
1703 /* test for jmonroe */
1705 /* don't approx lightmaps with styled twins */
1706 if ( lm->numStyledTwins > 0 ) {
1710 /* don't approx lightmaps with styles */
1711 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1713 if ( lm->styles[ i ] != LS_NONE ) {
1719 /* assume reduced until shadow detail is found */
1720 approximated = qtrue;
1722 /* walk the list of surfaces on this raw lightmap */
1723 for ( n = 0; n < lm->numLightSurfaces; n++ )
1726 num = lightSurfaces[ lm->firstLightSurface + n ];
1727 ds = &bspDrawSurfaces[ num ];
1728 info = &surfaceInfos[ num ];
1730 /* assume not-reduced initially */
1731 info->approximated = qfalse;
1733 /* bail if lightmap doesn't match up */
1734 if ( info->lm != lm ) {
1738 /* bail if not vertex lit */
1739 if ( info->si->noVertexLight ) {
1743 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1744 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1745 ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1746 ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1747 info->approximated = qtrue;
1748 numSurfsVertexForced++;
1752 /* handle the triangles */
1753 switch ( ds->surfaceType )
1757 verts = yDrawVerts + ds->firstVert;
1759 /* map the triangles */
1760 info->approximated = qtrue;
1761 for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1763 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1764 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1765 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1766 info->approximated = ApproximateTriangle_r( lm, dv );
1771 /* make a mesh from the drawsurf */
1772 src.width = ds->patchWidth;
1773 src.height = ds->patchHeight;
1774 src.verts = &yDrawVerts[ ds->firstVert ];
1775 //% subdivided = SubdivideMesh( src, 8, 512 );
1776 subdivided = SubdivideMesh2( src, info->patchIterations );
1778 /* fit it to the curve and remove colinear verts on rows/columns */
1779 PutMeshOnCurve( *subdivided );
1780 mesh = RemoveLinearMeshColumnsRows( subdivided );
1781 FreeMesh( subdivided );
1784 verts = mesh->verts;
1786 /* map the mesh quads */
1787 info->approximated = qtrue;
1788 for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1790 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1793 pw[ 0 ] = x + ( y * mesh->width );
1794 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1795 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1796 pw[ 3 ] = x + 1 + ( y * mesh->width );
1797 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1802 /* get drawverts and map first triangle */
1803 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1804 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1805 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1806 info->approximated = ApproximateTriangle_r( lm, dv );
1808 /* get drawverts and map second triangle */
1809 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1810 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1811 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1812 if ( info->approximated ) {
1813 info->approximated = ApproximateTriangle_r( lm, dv );
1827 if ( info->approximated == qfalse ) {
1828 approximated = qfalse;
1831 numSurfsVertexApproximated++;
1836 return approximated;
1842 TestOutLightmapStamp()
1843 tests a stamp on a given lightmap for validity
1846 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1847 int sx, sy, ox, oy, offset;
1852 if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1856 /* solid lightmaps test a 1x1 stamp */
1857 if ( lm->solid[ lightmapNum ] ) {
1858 offset = ( y * olm->customWidth ) + x;
1859 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1865 /* test the stamp */
1866 for ( sy = 0; sy < lm->h; sy++ )
1868 for ( sx = 0; sx < lm->w; sx++ )
1871 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1872 if ( luxel[ 0 ] < 0.0f ) {
1876 /* get bsp lightmap coords and test */
1879 offset = ( oy * olm->customWidth ) + ox;
1880 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1886 /* stamp is empty */
1894 sets up an output lightmap
1897 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1899 if ( lm == NULL || olm == NULL ) {
1903 /* is this a "normal" bsp-stored lightmap? */
1904 if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1905 olm->lightmapNum = numBSPLightmaps;
1908 /* lightmaps are interleaved with light direction maps */
1914 olm->lightmapNum = -3;
1917 /* set external lightmap number */
1918 olm->extLightmapNum = -1;
1921 olm->numLightmaps = 0;
1922 olm->customWidth = lm->customWidth;
1923 olm->customHeight = lm->customHeight;
1924 olm->freeLuxels = olm->customWidth * olm->customHeight;
1925 olm->numShaders = 0;
1927 /* allocate buffers */
1928 olm->lightBits = safe_malloc( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1929 memset( olm->lightBits, 0, ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1930 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1931 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1933 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1934 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1942 for a given surface lightmap, find output lightmap pages and positions for it
1945 static void FindOutLightmaps( rawLightmap_t *lm ){
1946 int i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
1948 surfaceInfo_t *info;
1949 float *luxel, *deluxel;
1950 vec3_t color, direction;
1955 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1956 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1957 lm->outLightmapNums[ lightmapNum ] = -3;
1959 /* can this lightmap be approximated with vertex color? */
1960 if ( ApproximateLightmap( lm ) ) {
1965 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1968 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1972 /* don't store twinned lightmaps */
1973 if ( lm->twins[ lightmapNum ] != NULL ) {
1977 /* if this is a styled lightmap, try some normalized locations first */
1979 if ( lightmapNum > 0 && outLightmaps != NULL ) {
1981 for ( j = 0; j < 2; j++ )
1983 /* try identical position */
1984 for ( i = 0; i < numOutLightmaps; i++ )
1986 /* get the output lightmap */
1987 olm = &outLightmaps[ i ];
1989 /* simple early out test */
1990 if ( olm->freeLuxels < lm->used ) {
1994 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
1995 if ( olm->customWidth != lm->customWidth ||
1996 olm->customHeight != lm->customHeight ) {
2002 x = lm->lightmapX[ 0 ];
2003 y = lm->lightmapY[ 0 ];
2004 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2010 for ( sy = -1; sy <= 1; sy++ )
2012 for ( sx = -1; sx <= 1; sx++ )
2014 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
2015 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
2016 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2040 /* try normal placement algorithm */
2041 if ( ok == qfalse ) {
2046 /* walk the list of lightmap pages */
2047 for ( i = 0; i < numOutLightmaps; i++ )
2049 /* get the output lightmap */
2050 olm = &outLightmaps[ i ];
2052 /* simple early out test */
2053 if ( olm->freeLuxels < lm->used ) {
2057 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2058 if ( olm->customWidth != lm->customWidth ||
2059 olm->customHeight != lm->customHeight ) {
2064 if ( lm->solid[ lightmapNum ] ) {
2065 xMax = olm->customWidth;
2066 yMax = olm->customHeight;
2070 xMax = ( olm->customWidth - lm->w ) + 1;
2071 yMax = ( olm->customHeight - lm->h ) + 1;
2074 /* walk the origin around the lightmap */
2075 for ( y = 0; y < yMax; y++ )
2077 for ( x = 0; x < xMax; x++ )
2079 /* find a fine tract of lauhnd */
2080 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2103 if ( ok == qfalse ) {
2104 /* allocate two new output lightmaps */
2105 numOutLightmaps += 2;
2106 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2107 if ( outLightmaps != NULL && numOutLightmaps > 2 ) {
2108 memcpy( olm, outLightmaps, ( numOutLightmaps - 2 ) * sizeof( outLightmap_t ) );
2109 free( outLightmaps );
2113 /* initialize both out lightmaps */
2114 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
2115 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
2117 /* set out lightmap */
2118 i = numOutLightmaps - 2;
2119 olm = &outLightmaps[ i ];
2121 /* set stamp xy origin to the first surface lightmap */
2122 if ( lightmapNum > 0 ) {
2123 x = lm->lightmapX[ 0 ];
2124 y = lm->lightmapY[ 0 ];
2128 /* if this is a style-using lightmap, it must be exported */
2129 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2130 olm->extLightmapNum = 0;
2133 /* add the surface lightmap to the bsp lightmap */
2134 lm->outLightmapNums[ lightmapNum ] = i;
2135 lm->lightmapX[ lightmapNum ] = x;
2136 lm->lightmapY[ lightmapNum ] = y;
2137 olm->numLightmaps++;
2140 for ( i = 0; i < lm->numLightSurfaces; i++ )
2142 /* get surface info */
2143 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2145 /* test for shader */
2146 for ( j = 0; j < olm->numShaders; j++ )
2148 if ( olm->shaders[ j ] == info->si ) {
2153 /* if it doesn't exist, add it */
2154 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2155 olm->shaders[ olm->numShaders ] = info->si;
2157 numLightmapShaders++;
2162 if ( lm->solid[ lightmapNum ] ) {
2172 /* mark the bits used */
2173 for ( y = 0; y < yMax; y++ )
2175 for ( x = 0; x < xMax; x++ )
2178 luxel = BSP_LUXEL( lightmapNum, x, y );
2179 deluxel = BSP_DELUXEL( x, y );
2180 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2184 /* set minimum light */
2185 if ( lm->solid[ lightmapNum ] ) {
2187 VectorSet( color, 255.0f, 0.0f, 0.0f );
2190 VectorCopy( lm->solidColor[ lightmapNum ], color );
2194 VectorCopy( luxel, color );
2197 /* styles are not affected by minlight */
2198 if ( lightmapNum == 0 ) {
2199 for ( i = 0; i < 3; i++ )
2201 if ( color[ i ] < minLight[ i ] ) {
2202 color[ i ] = minLight[ i ];
2207 /* get bsp lightmap coords */
2208 ox = x + lm->lightmapX[ lightmapNum ];
2209 oy = y + lm->lightmapY[ lightmapNum ];
2210 offset = ( oy * olm->customWidth ) + ox;
2212 /* flag pixel as used */
2213 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2217 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2218 ColorToBytes( color, pixel, lm->brightness );
2220 /* store direction */
2222 /* normalize average light direction */
2223 if ( VectorNormalize( deluxel, direction ) ) {
2224 /* encode [-1,1] in [0,255] */
2225 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2226 for ( i = 0; i < 3; i++ )
2228 temp = ( direction[ i ] + 1.0f ) * 127.5f;
2232 else if ( temp > 255 ) {
2249 CompareRawLightmap()
2250 compare function for qsort()
2253 static int CompareRawLightmap( const void *a, const void *b ){
2254 rawLightmap_t *alm, *blm;
2255 surfaceInfo_t *aInfo, *bInfo;
2260 alm = &rawLightmaps[ *( (int*) a ) ];
2261 blm = &rawLightmaps[ *( (int*) b ) ];
2263 /* get min number of surfaces */
2264 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2267 for ( i = 0; i < min; i++ )
2269 /* get surface info */
2270 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2271 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2273 /* compare shader names */
2274 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2280 /* test style count */
2282 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2283 diff += blm->styles[ i ] - alm->styles[ i ];
2289 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2294 /* must be equivalent */
2301 StoreSurfaceLightmaps()
2302 stores the surface lightmaps into the bsp as byte rgb triplets
2305 void StoreSurfaceLightmaps( void ){
2306 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2307 int style, size, lightmapNum, lightmapNum2;
2308 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2309 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2310 float *deluxel, *bspDeluxel, *bspDeluxel2;
2312 int numUsed, numTwins, numTwinLuxels, numStored;
2313 float lmx, lmy, efficiency;
2315 bspDrawSurface_t *ds, *parent, dsTemp;
2316 surfaceInfo_t *info;
2317 rawLightmap_t *lm, *lm2;
2319 bspDrawVert_t *dv, *ydv, *dvParent;
2320 char dirname[ 1024 ], filename[ 1024 ];
2322 char lightmapName[ 128 ];
2323 char *rgbGenValues[ 256 ];
2324 char *alphaGenValues[ 256 ];
2328 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2331 strcpy( dirname, source );
2332 StripExtension( dirname );
2333 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2334 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2336 /* -----------------------------------------------------------------
2337 average the sampled luxels into the bsp luxels
2338 ----------------------------------------------------------------- */
2341 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2343 /* walk the list of raw lightmaps */
2347 numSolidLightmaps = 0;
2348 for ( i = 0; i < numRawLightmaps; i++ )
2351 lm = &rawLightmaps[ i ];
2353 /* walk individual lightmaps */
2354 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2357 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2361 /* allocate bsp luxel storage */
2362 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2363 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2364 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2365 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2368 /* allocate radiosity lightmap storage */
2370 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2371 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2372 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2374 memset( lm->radLuxels[ lightmapNum ], 0, size );
2377 /* average supersampled luxels */
2378 for ( y = 0; y < lm->h; y++ )
2380 for ( x = 0; x < lm->w; x++ )
2384 occludedSamples = 0.0f;
2386 VectorClear( sample );
2387 VectorClear( occludedSample );
2388 VectorClear( dirSample );
2389 for ( ly = 0; ly < superSample; ly++ )
2391 for ( lx = 0; lx < superSample; lx++ )
2394 sx = x * superSample + lx;
2395 sy = y * superSample + ly;
2396 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2397 deluxel = SUPER_DELUXEL( sx, sy );
2398 normal = SUPER_NORMAL( sx, sy );
2399 cluster = SUPER_CLUSTER( sx, sy );
2401 /* sample deluxemap */
2402 if ( deluxemap && lightmapNum == 0 ) {
2403 VectorAdd( dirSample, deluxel, dirSample );
2406 /* keep track of used/occluded samples */
2407 if ( *cluster != CLUSTER_UNMAPPED ) {
2411 /* handle lightmap border? */
2412 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2413 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2418 else if ( debug && *cluster < 0 ) {
2419 if ( *cluster == CLUSTER_UNMAPPED ) {
2420 VectorSet( luxel, 255, 204, 0 );
2422 else if ( *cluster == CLUSTER_OCCLUDED ) {
2423 VectorSet( luxel, 255, 0, 255 );
2425 else if ( *cluster == CLUSTER_FLOODED ) {
2426 VectorSet( luxel, 0, 32, 255 );
2428 VectorAdd( occludedSample, luxel, occludedSample );
2429 occludedSamples += 1.0f;
2432 /* normal luxel handling */
2433 else if ( luxel[ 3 ] > 0.0f ) {
2434 /* handle lit or flooded luxels */
2435 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2436 VectorAdd( sample, luxel, sample );
2437 samples += luxel[ 3 ];
2440 /* handle occluded or unmapped luxels */
2443 VectorAdd( occludedSample, luxel, occludedSample );
2444 occludedSamples += luxel[ 3 ];
2447 /* handle style debugging */
2448 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2449 VectorCopy( debugColors[ 0 ], sample );
2456 /* only use occluded samples if necessary */
2457 if ( samples <= 0.0f ) {
2458 VectorCopy( occludedSample, sample );
2459 samples = occludedSamples;
2463 luxel = SUPER_LUXEL( lightmapNum, x, y );
2464 deluxel = SUPER_DELUXEL( x, y );
2466 /* store light direction */
2467 if ( deluxemap && lightmapNum == 0 ) {
2468 VectorCopy( dirSample, deluxel );
2471 /* store the sample back in super luxels */
2472 if ( samples > 0.01f ) {
2473 VectorScale( sample, ( 1.0f / samples ), luxel );
2477 /* if any samples were mapped in any way, store ambient color */
2478 else if ( mappedSamples > 0 ) {
2479 if ( lightmapNum == 0 ) {
2480 VectorCopy( ambientColor, luxel );
2483 VectorClear( luxel );
2488 /* store a bogus value to be fixed later */
2491 VectorClear( luxel );
2499 ClearBounds( colorMins, colorMaxs );
2501 /* clean up and store into bsp luxels */
2502 for ( y = 0; y < lm->h; y++ )
2504 for ( x = 0; x < lm->w; x++ )
2507 luxel = SUPER_LUXEL( lightmapNum, x, y );
2508 deluxel = SUPER_DELUXEL( x, y );
2510 /* copy light direction */
2511 if ( deluxemap && lightmapNum == 0 ) {
2512 VectorCopy( deluxel, dirSample );
2515 /* is this a valid sample? */
2516 if ( luxel[ 3 ] > 0.0f ) {
2517 VectorCopy( luxel, sample );
2518 samples = luxel[ 3 ];
2522 /* fix negative samples */
2523 for ( j = 0; j < 3; j++ )
2525 if ( sample[ j ] < 0.0f ) {
2532 /* nick an average value from the neighbors */
2533 VectorClear( sample );
2534 VectorClear( dirSample );
2537 /* fixme: why is this disabled?? */
2538 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2540 if ( sy < 0 || sy >= lm->h ) {
2544 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2546 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2550 /* get neighbor's particulars */
2551 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2552 if ( luxel[ 3 ] < 0.0f ) {
2555 VectorAdd( sample, luxel, sample );
2556 samples += luxel[ 3 ];
2561 if ( samples == 0.0f ) {
2562 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2570 /* fix negative samples */
2571 for ( j = 0; j < 3; j++ )
2573 if ( sample[ j ] < 0.0f ) {
2580 /* scale the sample */
2581 VectorScale( sample, ( 1.0f / samples ), sample );
2583 /* store the sample in the radiosity luxels */
2585 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2586 VectorCopy( sample, radLuxel );
2588 /* if only storing bounced light, early out here */
2589 if ( bounceOnly && !bouncing ) {
2594 /* store the sample in the bsp luxels */
2595 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2596 bspDeluxel = BSP_DELUXEL( x, y );
2598 VectorAdd( bspLuxel, sample, bspLuxel );
2599 if ( deluxemap && lightmapNum == 0 ) {
2600 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2603 /* add color to bounds for solid checking */
2604 if ( samples > 0.0f ) {
2605 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2610 /* set solid color */
2611 lm->solid[ lightmapNum ] = qfalse;
2612 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2613 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2615 /* nocollapse prevents solid lightmaps */
2616 if ( noCollapse == qfalse ) {
2617 /* check solid color */
2618 VectorSubtract( colorMaxs, colorMins, sample );
2619 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2620 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2622 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2623 lm->solid[ lightmapNum ] = qtrue;
2624 numSolidLightmaps++;
2627 /* if all lightmaps aren't solid, then none of them are solid */
2628 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2629 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2631 if ( lm->solid[ y ] ) {
2632 numSolidLightmaps--;
2634 lm->solid[ y ] = qfalse;
2639 /* wrap bsp luxels if necessary */
2640 if ( lm->wrap[ 0 ] ) {
2641 for ( y = 0; y < lm->h; y++ )
2643 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2644 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2645 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2646 VectorScale( bspLuxel, 0.5f, bspLuxel );
2647 VectorCopy( bspLuxel, bspLuxel2 );
2648 if ( deluxemap && lightmapNum == 0 ) {
2649 bspDeluxel = BSP_DELUXEL( 0, y );
2650 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2651 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2652 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2653 VectorCopy( bspDeluxel, bspDeluxel2 );
2657 if ( lm->wrap[ 1 ] ) {
2658 for ( x = 0; x < lm->w; x++ )
2660 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2661 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2662 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2663 VectorScale( bspLuxel, 0.5f, bspLuxel );
2664 VectorCopy( bspLuxel, bspLuxel2 );
2665 if ( deluxemap && lightmapNum == 0 ) {
2666 bspDeluxel = BSP_DELUXEL( x, 0 );
2667 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2668 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2669 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2670 VectorCopy( bspDeluxel, bspDeluxel2 );
2677 /* -----------------------------------------------------------------
2678 collapse non-unique lightmaps
2679 ----------------------------------------------------------------- */
2681 if ( noCollapse == qfalse && deluxemap == qfalse ) {
2683 Sys_FPrintf( SYS_VRB, "collapsing..." );
2685 /* set all twin refs to null */
2686 for ( i = 0; i < numRawLightmaps; i++ )
2688 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2690 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2691 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2692 rawLightmaps[ i ].numStyledTwins = 0;
2696 /* walk the list of raw lightmaps */
2697 for ( i = 0; i < numRawLightmaps; i++ )
2700 lm = &rawLightmaps[ i ];
2702 /* walk lightmaps */
2703 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2706 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
2707 lm->twins[ lightmapNum ] != NULL ) {
2711 /* find all lightmaps that are virtually identical to this one */
2712 for ( j = i + 1; j < numRawLightmaps; j++ )
2715 lm2 = &rawLightmaps[ j ];
2717 /* walk lightmaps */
2718 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
2721 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
2722 lm2->twins[ lightmapNum2 ] != NULL ) {
2727 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
2728 /* merge and set twin */
2729 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
2730 lm2->twins[ lightmapNum2 ] = lm;
2731 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
2733 numTwinLuxels += ( lm->w * lm->h );
2735 /* count styled twins */
2736 if ( lightmapNum > 0 ) {
2737 lm->numStyledTwins++;
2747 /* -----------------------------------------------------------------
2748 sort raw lightmaps by shader
2749 ----------------------------------------------------------------- */
2752 Sys_FPrintf( SYS_VRB, "sorting..." );
2754 /* allocate a new sorted list */
2755 if ( sortLightmaps == NULL ) {
2756 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
2759 /* fill it out and sort it */
2760 for ( i = 0; i < numRawLightmaps; i++ )
2761 sortLightmaps[ i ] = i;
2762 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
2764 /* -----------------------------------------------------------------
2765 allocate output lightmaps
2766 ----------------------------------------------------------------- */
2769 Sys_FPrintf( SYS_VRB, "allocating..." );
2771 /* kill all existing output lightmaps */
2772 if ( outLightmaps != NULL ) {
2773 for ( i = 0; i < numOutLightmaps; i++ )
2775 free( outLightmaps[ i ].lightBits );
2776 free( outLightmaps[ i ].bspLightBytes );
2778 free( outLightmaps );
2779 outLightmaps = NULL;
2782 numLightmapShaders = 0;
2783 numOutLightmaps = 0;
2784 numBSPLightmaps = 0;
2785 numExtLightmaps = 0;
2787 /* find output lightmap */
2788 for ( i = 0; i < numRawLightmaps; i++ )
2790 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2791 FindOutLightmaps( lm );
2794 /* set output numbers in twinned lightmaps */
2795 for ( i = 0; i < numRawLightmaps; i++ )
2798 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2800 /* walk lightmaps */
2801 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2804 lm2 = lm->twins[ lightmapNum ];
2805 if ( lm2 == NULL ) {
2808 lightmapNum2 = lm->twinNums[ lightmapNum ];
2810 /* find output lightmap from twin */
2811 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
2812 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
2813 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
2817 /* -----------------------------------------------------------------
2818 store output lightmaps
2819 ----------------------------------------------------------------- */
2822 Sys_FPrintf( SYS_VRB, "storing..." );
2824 /* count the bsp lightmaps and allocate space */
2825 if ( bspLightBytes != NULL ) {
2826 free( bspLightBytes );
2828 if ( numBSPLightmaps == 0 || externalLightmaps ) {
2829 numBSPLightBytes = 0;
2830 bspLightBytes = NULL;
2834 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
2835 bspLightBytes = safe_malloc( numBSPLightBytes );
2836 memset( bspLightBytes, 0, numBSPLightBytes );
2839 /* walk the list of output lightmaps */
2840 for ( i = 0; i < numOutLightmaps; i++ )
2842 /* get output lightmap */
2843 olm = &outLightmaps[ i ];
2845 /* is this a valid bsp lightmap? */
2846 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
2847 /* copy lighting data */
2848 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
2849 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
2851 /* copy direction data */
2853 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
2854 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
2858 /* external lightmap? */
2859 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
2860 /* make a directory for the lightmaps */
2863 /* set external lightmap number */
2864 olm->extLightmapNum = numExtLightmaps;
2866 /* write lightmap */
2867 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2868 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2869 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
2872 /* write deluxemap */
2874 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2875 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2876 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
2879 if ( debugDeluxemap ) {
2880 olm->extLightmapNum++;
2886 if ( numExtLightmaps > 0 ) {
2887 Sys_FPrintf( SYS_VRB, "\n" );
2890 /* delete unused external lightmaps */
2891 for ( i = numExtLightmaps; i; i++ )
2893 /* determine if file exists */
2894 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
2895 if ( !FileExists( filename ) ) {
2903 /* -----------------------------------------------------------------
2904 project the lightmaps onto the bsp surfaces
2905 ----------------------------------------------------------------- */
2908 Sys_FPrintf( SYS_VRB, "projecting..." );
2910 /* walk the list of surfaces */
2911 for ( i = 0; i < numBSPDrawSurfaces; i++ )
2913 /* get the surface and info */
2914 ds = &bspDrawSurfaces[ i ];
2915 info = &surfaceInfos[ i ];
2919 /* handle surfaces with identical parent */
2920 if ( info->parentSurfaceNum >= 0 ) {
2921 /* preserve original data and get parent */
2922 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
2923 memcpy( &dsTemp, ds, sizeof( *ds ) );
2925 /* overwrite child with parent data */
2926 memcpy( ds, parent, sizeof( *ds ) );
2928 /* restore key parts */
2929 ds->fogNum = dsTemp.fogNum;
2930 ds->firstVert = dsTemp.firstVert;
2931 ds->firstIndex = dsTemp.firstIndex;
2932 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
2934 /* set vertex data */
2935 dv = &bspDrawVerts[ ds->firstVert ];
2936 dvParent = &bspDrawVerts[ parent->firstVert ];
2937 for ( j = 0; j < ds->numVerts; j++ )
2939 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
2940 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
2947 /* handle vertex lit or approximated surfaces */
2948 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
2949 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2951 ds->lightmapNum[ lightmapNum ] = -3;
2952 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
2956 /* handle lightmapped surfaces */
2959 /* walk lightmaps */
2960 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2963 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2965 /* handle unused style */
2966 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
2967 ds->lightmapNum[ lightmapNum ] = -3;
2971 /* get output lightmap */
2972 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
2974 /* set bsp lightmap number */
2975 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
2977 /* deluxemap debugging makes the deluxemap visible */
2978 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
2979 ds->lightmapNum[ lightmapNum ]++;
2982 /* calc lightmap origin in texture space */
2983 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
2984 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
2986 /* calc lightmap st coords */
2987 dv = &bspDrawVerts[ ds->firstVert ];
2988 ydv = &yDrawVerts[ ds->firstVert ];
2989 for ( j = 0; j < ds->numVerts; j++ )
2991 if ( lm->solid[ lightmapNum ] ) {
2992 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
2993 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
2997 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
2998 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3004 /* store vertex colors */
3005 dv = &bspDrawVerts[ ds->firstVert ];
3006 for ( j = 0; j < ds->numVerts; j++ )
3008 /* walk lightmaps */
3009 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3011 /* handle unused style */
3012 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3013 VectorClear( color );
3017 /* get vertex color */
3018 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3019 VectorCopy( luxel, color );
3021 /* set minimum light */
3022 if ( lightmapNum == 0 ) {
3023 for ( k = 0; k < 3; k++ )
3024 if ( color[ k ] < minVertexLight[ k ] ) {
3025 color[ k ] = minVertexLight[ k ];
3030 /* store to bytes */
3031 if ( !info->si->noVertexLight ) {
3032 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3037 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3038 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3040 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3044 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3045 dv = &bspDrawVerts[ ds->firstVert ];
3047 /* depthFunc equal? */
3048 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3055 /* generate stages for styled lightmaps */
3056 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3059 style = lm->styles[ lightmapNum ];
3060 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3064 /* get output lightmap */
3065 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3068 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3069 strcpy( lightmapName, "$lightmap" );
3072 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3075 /* get rgbgen string */
3076 if ( rgbGenValues[ style ] == NULL ) {
3077 sprintf( key, "_style%drgbgen", style );
3078 rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3079 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3080 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3084 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3085 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3091 /* get alphagen string */
3092 if ( alphaGenValues[ style ] == NULL ) {
3093 sprintf( key, "_style%dalphagen", style );
3094 alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3096 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3097 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3100 alphaGen[ 0 ] = '\0';
3103 /* calculate st offset */
3104 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3105 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3107 /* create additional stage */
3108 if ( lmx == 0.0f && lmy == 0.0f ) {
3109 sprintf( styleStage, "\t{\n"
3110 "\t\tmap %s\n" /* lightmap */
3111 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3112 "%s" /* depthFunc equal */
3115 "\t\ttcGen lightmap\n"
3118 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3124 sprintf( styleStage, "\t{\n"
3125 "\t\tmap %s\n" /* lightmap */
3126 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3127 "%s" /* depthFunc equal */
3130 "\t\ttcGen lightmap\n"
3131 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3134 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3142 strcat( styleStages, styleStage );
3145 /* create custom shader */
3146 if ( info->si->styleMarker == 2 ) {
3147 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3150 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3153 /* emit remap command */
3154 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3157 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3158 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3159 //% Sys_Printf( ")\n" );
3162 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3163 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3164 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3165 /* get output lightmap */
3166 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3168 /* do some name mangling */
3169 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3171 /* create custom shader */
3172 csi = CustomShader( info->si, "$lightmap", lightmapName );
3175 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3176 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3177 //% Sys_Printf( ")\n" );
3180 /* use the normal plain-jane shader */
3182 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3187 Sys_FPrintf( SYS_VRB, "done.\n" );
3189 /* calc num stored */
3190 numStored = numBSPLightBytes / 3;
3191 efficiency = ( numStored <= 0 )
3193 : (float) numUsed / (float) numStored;
3196 Sys_Printf( "%9d luxels used\n", numUsed );
3197 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3198 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3199 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3200 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3201 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3202 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3203 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3204 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3206 /* write map shader file */
3207 WriteMapShaderFile();