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 /* we may also use safe_malloc0 on the whole instead,
71 * this would just be a bit slower */
72 memset( buffer, 0, 18 );
74 buffer[ 12 ] = width & 255;
75 buffer[ 13 ] = width >> 8;
76 buffer[ 14 ] = height & 255;
77 buffer[ 15 ] = height >> 8;
81 c = ( width * height * 3 ) + 18;
82 for ( i = 18; i < c; i += 3 )
84 buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
85 buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
86 buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
89 /* write it and free the buffer */
90 file = fopen( filename, "wb" );
92 Error( "Unable to open %s for writing", filename );
95 /* flip vertically? */
97 fwrite( buffer, 1, 18, file );
98 for ( in = buffer + ( ( height - 1 ) * width * 3 ) + 18; in >= buffer; in -= ( width * 3 ) )
99 fwrite( in, 1, ( width * 3 ), file );
102 fwrite( buffer, 1, c, file );
114 exports the lightmaps as a list of numbered tga images
117 void ExportLightmaps( void ){
119 char dirname[ 1024 ], filename[ 1024 ];
124 Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n" );
126 /* do some path mangling */
127 strcpy( dirname, source );
128 StripExtension( dirname );
131 if ( bspLightBytes == NULL ) {
132 Sys_FPrintf( SYS_WRN, "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 ){
159 Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
163 /* do some path mangling */
164 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
165 StripExtension( source );
166 DefaultExtension( source, ".bsp" );
169 Sys_Printf( "Loading %s\n", source );
170 LoadBSPFile( source );
172 /* export the lightmaps */
175 /* return to sender */
182 ImportLightmapsMain()
183 imports the lightmaps from a list of numbered tga images
186 int ImportLightmapsMain( int argc, char **argv ){
187 int i, x, y, len, width, height;
188 char dirname[ 1024 ], filename[ 1024 ];
189 byte *lightmap, *buffer, *pixels, *in, *out;
194 Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
198 /* do some path mangling */
199 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
200 StripExtension( source );
201 DefaultExtension( source, ".bsp" );
204 Sys_Printf( "Loading %s\n", source );
205 LoadBSPFile( source );
208 Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n" );
210 /* do some path mangling */
211 strcpy( dirname, source );
212 StripExtension( dirname );
215 if ( bspLightBytes == NULL ) {
216 Error( "No lightmap data" );
219 /* make a directory for the lightmaps */
222 /* iterate through the lightmaps */
223 for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
225 /* read a tga image */
226 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
227 Sys_Printf( "Loading %s\n", filename );
229 len = vfsLoadFile( filename, (void*) &buffer, -1 );
231 Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
235 /* parse file into an image */
237 LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
240 /* sanity check it */
241 if ( pixels == NULL ) {
242 Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
245 if ( width != game->lightmapSize || height != game->lightmapSize ) {
246 Sys_FPrintf( SYS_WRN, "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
247 filename, width, height, game->lightmapSize, game->lightmapSize );
250 /* copy the pixels */
252 for ( y = 1; y <= game->lightmapSize; y++ )
254 out = lightmap + ( ( game->lightmapSize - y ) * game->lightmapSize * 3 );
255 for ( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
256 VectorCopy( in, out );
264 Sys_Printf( "writing %s\n", source );
265 WriteBSPFile( source );
267 /* return to sender */
273 /* -------------------------------------------------------------------------------
275 this section deals with projecting a lightmap onto a raw drawsurface
277 ------------------------------------------------------------------------------- */
280 CompareLightSurface()
281 compare function for qsort()
284 static int CompareLightSurface( const void *a, const void *b ){
285 shaderInfo_t *asi, *bsi;
289 asi = surfaceInfos[ *( (const int*) a ) ].si;
290 bsi = surfaceInfos[ *( (const int*) b ) ].si;
300 /* compare shader names */
301 return strcmp( asi->shader, bsi->shader );
308 allocates a raw lightmap's necessary buffers
311 void FinishRawLightmap( rawLightmap_t *lm ){
312 int i, j, c, size, *sc;
317 /* sort light surfaces by shader name */
318 qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
321 lm->numLightClusters = 0;
322 for ( i = 0; i < lm->numLightSurfaces; i++ )
324 /* get surface info */
325 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
327 /* add surface clusters */
328 lm->numLightClusters += info->numSurfaceClusters;
331 /* allocate buffer for clusters and copy */
332 lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
334 for ( i = 0; i < lm->numLightSurfaces; i++ )
336 /* get surface info */
337 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
339 /* add surface clusters */
340 for ( j = 0; j < info->numSurfaceClusters; j++ )
341 lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
345 lm->styles[ 0 ] = LS_NORMAL;
346 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
347 lm->styles[ i ] = LS_NONE;
349 /* set supersampling size */
350 lm->sw = lm->w * superSample;
351 lm->sh = lm->h * superSample;
353 /* add to super luxel count */
354 numRawSuperLuxels += ( lm->sw * lm->sh );
356 /* manipulate origin/vecs for supersampling */
357 if ( superSample > 1 && lm->vecs != NULL ) {
358 /* calc inverse supersample */
359 is = 1.0f / superSample;
361 /* scale the vectors and shift the origin */
363 /* new code that works for arbitrary supersampling values */
364 VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
365 VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
366 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
367 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
368 VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
369 VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
371 /* old code that only worked with a value of 2 */
372 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
373 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
374 VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
375 VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
379 /* allocate bsp lightmap storage */
380 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
381 if ( lm->bspLuxels[ 0 ] == NULL ) {
382 lm->bspLuxels[ 0 ] = safe_malloc( size );
384 memset( lm->bspLuxels[ 0 ], 0, size );
386 /* allocate radiosity lightmap storage */
388 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
389 if ( lm->radLuxels[ 0 ] == NULL ) {
390 lm->radLuxels[ 0 ] = safe_malloc( size );
392 memset( lm->radLuxels[ 0 ], 0, size );
395 /* allocate sampling lightmap storage */
396 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
397 if ( lm->superLuxels[ 0 ] == NULL ) {
398 lm->superLuxels[ 0 ] = safe_malloc( size );
400 memset( lm->superLuxels[ 0 ], 0, size );
402 /* allocate origin map storage */
403 size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
404 if ( lm->superOrigins == NULL ) {
405 lm->superOrigins = safe_malloc( size );
407 memset( lm->superOrigins, 0, size );
409 /* allocate normal map storage */
410 size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
411 if ( lm->superNormals == NULL ) {
412 lm->superNormals = safe_malloc( size );
414 memset( lm->superNormals, 0, size );
416 /* allocate floodlight map storage */
417 size = lm->sw * lm->sh * SUPER_FLOODLIGHT_SIZE * sizeof( float );
418 if ( lm->superFloodLight == NULL ) {
419 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 );
428 size = lm->sw * lm->sh;
429 sc = lm->superClusters;
430 for ( i = 0; i < size; i++ )
431 ( *sc++ ) = CLUSTER_UNMAPPED;
433 /* 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 );
440 memset( lm->superDeluxels, 0, size );
442 /* allocate bsp deluxel storage */
443 size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
444 if ( lm->bspDeluxels == NULL ) {
445 lm->bspDeluxels = safe_malloc( size );
447 memset( lm->bspDeluxels, 0, size );
451 numLuxels += ( lm->sw * lm->sh );
457 AddPatchToRawLightmap()
458 projects a lightmap for a patch surface
459 since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
460 it is no longer necessary for patch verts to fall exactly on a lightmap sample
461 based on AllocateLightmapForPatch()
464 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm ){
465 bspDrawSurface_t *ds;
468 bspDrawVert_t *verts, *a, *b;
470 mesh_t src, *subdivided, *mesh;
471 float sBasis, tBasis, s, t;
472 float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
475 /* patches finish a raw lightmap */
476 lm->finished = qtrue;
478 /* get surface and info */
479 ds = &bspDrawSurfaces[ num ];
480 info = &surfaceInfos[ num ];
482 /* make a temporary mesh from the drawsurf */
483 src.width = ds->patchWidth;
484 src.height = ds->patchHeight;
485 src.verts = &yDrawVerts[ ds->firstVert ];
486 //% subdivided = SubdivideMesh( src, 8, 512 );
487 subdivided = SubdivideMesh2( src, info->patchIterations );
489 /* fit it to the curve and remove colinear verts on rows/columns */
490 PutMeshOnCurve( *subdivided );
491 mesh = RemoveLinearMeshColumnsRows( subdivided );
492 FreeMesh( subdivided );
494 /* find the longest distance on each row/column */
496 memset( widthTable, 0, sizeof( widthTable ) );
497 memset( heightTable, 0, sizeof( heightTable ) );
498 for ( y = 0; y < mesh->height; y++ )
500 for ( x = 0; x < mesh->width; x++ )
503 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;
514 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;
526 /* determine lightmap width */
528 for ( x = 0; x < ( mesh->width - 1 ); x++ )
529 length += widthTable[ x ];
530 lm->w = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
531 if ( lm->w < ds->patchWidth ) {
532 lm->w = ds->patchWidth;
534 if ( lm->w > lm->customWidth ) {
535 lm->w = lm->customWidth;
537 sBasis = (float) ( lm->w - 1 ) / (float) ( ds->patchWidth - 1 );
539 /* determine lightmap height */
541 for ( y = 0; y < ( mesh->height - 1 ); y++ )
542 length += heightTable[ y ];
543 lm->h = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
544 if ( lm->h < ds->patchHeight ) {
545 lm->h = ds->patchHeight;
547 if ( lm->h > lm->customHeight ) {
548 lm->h = lm->customHeight;
550 tBasis = (float) ( lm->h - 1 ) / (float) ( ds->patchHeight - 1 );
552 /* free the temporary mesh */
555 /* set the lightmap texture coordinates in yDrawVerts */
556 lm->wrap[ 0 ] = qtrue;
557 lm->wrap[ 1 ] = qtrue;
558 verts = &yDrawVerts[ ds->firstVert ];
559 for ( y = 0; y < ds->patchHeight; y++ )
561 t = ( tBasis * y ) + 0.5f;
562 for ( x = 0; x < ds->patchWidth; x++ )
564 s = ( sBasis * x ) + 0.5f;
565 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
566 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
568 if ( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ( ( ds->patchHeight - 1 ) * ds->patchWidth ) + x ].xyz ) ) {
569 lm->wrap[ 1 ] = qfalse;
573 if ( !VectorCompare( verts[ ( y * ds->patchWidth ) ].xyz, verts[ ( y * ds->patchWidth ) + ( ds->patchWidth - 1 ) ].xyz ) ) {
574 lm->wrap[ 0 ] = qfalse;
579 //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
580 //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
581 //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
582 //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
583 //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
586 numPatchesLightmapped++;
595 AddSurfaceToRawLightmap()
596 projects a lightmap for a surface
597 based on AllocateLightmapForSurface()
600 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm ){
601 bspDrawSurface_t *ds, *ds2;
603 int num2, n, i, axisNum;
604 float s, t, d, len, sampleSize;
605 vec3_t mins, maxs, origin, faxis, size, delta, normalized, vecs[ 2 ];
607 bspDrawVert_t *verts;
610 /* get surface and info */
611 ds = &bspDrawSurfaces[ num ];
612 info = &surfaceInfos[ num ];
614 /* add the surface to the raw lightmap */
615 lightSurfaces[ numLightSurfaces++ ] = num;
616 lm->numLightSurfaces++;
618 /* does this raw lightmap already have any surfaces? */
619 if ( lm->numLightSurfaces > 1 ) {
620 /* surface and raw lightmap must have the same lightmap projection axis */
621 if ( VectorCompare( info->axis, lm->axis ) == qfalse ) {
625 /* match identical attributes */
626 if ( info->sampleSize != lm->sampleSize ||
627 info->entityNum != lm->entityNum ||
628 info->recvShadows != lm->recvShadows ||
629 info->si->lmCustomWidth != lm->customWidth ||
630 info->si->lmCustomHeight != lm->customHeight ||
631 info->si->lmBrightness != lm->brightness ||
632 info->si->lmFilterRadius != lm->filterRadius ||
633 info->si->splotchFix != lm->splotchFix ) {
637 /* surface bounds must intersect with raw lightmap bounds */
638 for ( i = 0; i < 3; i++ )
640 if ( info->mins[ i ] > lm->maxs[ i ] ) {
643 if ( info->maxs[ i ] < lm->mins[ i ] ) {
648 /* plane check (fixme: allow merging of nonplanars) */
649 if ( info->si->lmMergable == qfalse ) {
650 if ( info->plane == NULL || lm->plane == NULL ) {
655 for ( i = 0; i < 4; i++ )
656 if ( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON ) {
661 /* debug code hacking */
662 //% if( lm->numLightSurfaces > 1 )
667 if ( info->plane == NULL ) {
671 /* add surface to lightmap bounds */
672 AddPointToBounds( info->mins, lm->mins, lm->maxs );
673 AddPointToBounds( info->maxs, lm->mins, lm->maxs );
675 /* check to see if this is a non-planar patch */
676 if ( ds->surfaceType == MST_PATCH &&
677 lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f ) {
678 return AddPatchToRawLightmap( num, lm );
681 /* start with initially requested sample size */
682 sampleSize = lm->sampleSize;
684 /* round to the lightmap resolution */
685 for ( i = 0; i < 3; i++ )
687 mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
688 maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
689 size[ i ] = ( maxs[ i ] - mins[ i ] ) / sampleSize + 1.0f;
691 /* hack (god this sucks) */
692 if ( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight || ( lmLimitSize && size[i] > lmLimitSize ) ) {
698 if ( sampleSize != lm->sampleSize && lmLimitSize == 0 ) {
699 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",
710 /* set actual sample size */
711 lm->actualSampleSize = sampleSize;
713 /* fixme: copy rounded mins/maxes to lightmap record? */
714 if ( lm->plane == NULL ) {
715 VectorCopy( mins, lm->mins );
716 VectorCopy( maxs, lm->maxs );
717 VectorCopy( mins, origin );
720 /* set lightmap origin */
721 VectorCopy( lm->mins, origin );
723 /* make absolute axis */
724 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
725 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
726 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
728 /* clear out lightmap vectors */
729 memset( vecs, 0, sizeof( vecs ) );
731 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
732 if ( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] ) {
736 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
737 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
739 else if ( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] ) {
743 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
744 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
751 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
752 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
755 /* check for bogus axis */
756 if ( faxis[ axisNum ] == 0.0f ) {
757 Sys_FPrintf( SYS_WRN, "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
762 /* store the axis number in the lightmap */
763 lm->axisNum = axisNum;
765 /* walk the list of surfaces on this raw lightmap */
766 for ( n = 0; n < lm->numLightSurfaces; n++ )
769 num2 = lightSurfaces[ lm->firstLightSurface + n ];
770 ds2 = &bspDrawSurfaces[ num2 ];
771 verts = &yDrawVerts[ ds2->firstVert ];
773 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
774 for ( i = 0; i < ds2->numVerts; i++ )
776 VectorSubtract( verts[ i ].xyz, origin, delta );
777 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
778 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
779 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
780 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
782 if ( s > (float) lm->w || t > (float) lm->h ) {
783 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
784 s, lm->w, t, lm->h );
789 /* get first drawsurface */
790 num2 = lightSurfaces[ lm->firstLightSurface ];
791 ds2 = &bspDrawSurfaces[ num2 ];
792 verts = &yDrawVerts[ ds2->firstVert ];
794 /* calculate lightmap origin */
795 if ( VectorLength( ds2->lightmapVecs[ 2 ] ) ) {
796 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
799 VectorCopy( lm->axis, plane );
801 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
803 VectorCopy( origin, lm->origin );
804 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
805 d /= plane[ axisNum ];
806 lm->origin[ axisNum ] -= d;
809 VectorCopy( lm->origin, ds->lightmapOrigin );
811 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
812 if ( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) { /* ydnar: can't remember what exactly i was thinking here... */
813 /* allocate space for the vectors */
814 lm->vecs = safe_malloc0( 3 * sizeof( vec3_t ) );
815 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
817 /* project stepped lightmap blocks and subtract to get planevecs */
818 for ( i = 0; i < 2; i++ )
820 len = VectorNormalize( vecs[ i ], normalized );
821 VectorScale( normalized, ( 1.0 / len ), lm->vecs[ i ] );
822 d = DotProduct( lm->vecs[ i ], plane );
823 d /= plane[ axisNum ];
824 lm->vecs[ i ][ axisNum ] -= d;
829 /* lightmap vectors are useless on a non-planar surface */
834 if ( ds->surfaceType == MST_PATCH ) {
835 numPatchesLightmapped++;
836 if ( lm->plane != NULL ) {
837 numPlanarPatchesLightmapped++;
842 if ( lm->plane != NULL ) {
843 numPlanarsLightmapped++;
846 numNonPlanarsLightmapped++;
858 compare function for qsort()
861 static int CompareSurfaceInfo( const void *a, const void *b ){
862 surfaceInfo_t *aInfo, *bInfo;
866 /* get surface info */
867 aInfo = &surfaceInfos[ *( (const int*) a ) ];
868 bInfo = &surfaceInfos[ *( (const int*) b ) ];
871 if ( aInfo->modelindex < bInfo->modelindex ) {
874 else if ( aInfo->modelindex > bInfo->modelindex ) {
878 /* then lightmap status */
879 if ( aInfo->hasLightmap < bInfo->hasLightmap ) {
882 else if ( aInfo->hasLightmap > bInfo->hasLightmap ) {
886 /* 27: then shader! */
887 if ( aInfo->si < bInfo->si ) {
890 else if ( aInfo->si > bInfo->si ) {
894 /* then lightmap sample size */
895 if ( aInfo->sampleSize < bInfo->sampleSize ) {
898 else if ( aInfo->sampleSize > bInfo->sampleSize ) {
902 /* then lightmap axis */
903 for ( i = 0; i < 3; i++ )
905 if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
908 else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
914 if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
917 else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
920 else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
921 for ( i = 0; i < 4; i++ )
923 if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
926 else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
932 /* then position in world */
933 for ( i = 0; i < 3; i++ )
935 if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
938 else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
943 /* these are functionally identical (this should almost never happen) */
950 SetupSurfaceLightmaps()
951 allocates lightmaps for every surface in the bsp that needs one
952 this depends on yDrawVerts being allocated
955 void SetupSurfaceLightmaps( void ){
956 int i, j, k, s,num, num2;
959 bspDrawSurface_t *ds;
960 surfaceInfo_t *info, *info2;
963 vec3_t mapSize, entityOrigin;
967 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
969 /* determine supersample amount */
970 if ( superSample < 1 ) {
973 else if ( superSample > 8 ) {
974 Sys_FPrintf( SYS_WRN, "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
978 /* clear map bounds */
979 ClearBounds( mapMins, mapMaxs );
981 /* allocate a list of surface clusters */
982 numSurfaceClusters = 0;
983 maxSurfaceClusters = numBSPLeafSurfaces;
984 surfaceClusters = safe_malloc0( maxSurfaceClusters * sizeof( *surfaceClusters ) );
986 /* allocate a list for per-surface info */
987 surfaceInfos = safe_malloc0( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
988 for ( i = 0; i < numBSPDrawSurfaces; i++ )
989 surfaceInfos[ i ].childSurfaceNum = -1;
991 /* allocate a list of surface indexes to be sorted */
992 sortSurfaces = safe_malloc0( numBSPDrawSurfaces * sizeof( int ) );
994 /* walk each model in the bsp */
995 for ( i = 0; i < numBSPModels; i++ )
998 model = &bspModels[ i ];
1000 /* walk the list of surfaces in this model and fill out the info structs */
1001 for ( j = 0; j < model->numBSPSurfaces; j++ )
1003 /* make surface index */
1004 num = model->firstBSPSurface + j;
1006 /* copy index to sort list */
1007 sortSurfaces[ num ] = num;
1009 /* get surface and info */
1010 ds = &bspDrawSurfaces[ num ];
1011 info = &surfaceInfos[ num ];
1013 /* set entity origin */
1014 if ( ds->numVerts > 0 ) {
1015 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
1018 VectorClear( entityOrigin );
1022 info->modelindex = i;
1025 info->firstSurfaceCluster = numSurfaceClusters;
1027 /* get extra data */
1028 info->si = GetSurfaceExtraShaderInfo( num );
1029 if ( info->si == NULL ) {
1030 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
1032 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1033 info->entityNum = GetSurfaceExtraEntityNum( num );
1034 info->castShadows = GetSurfaceExtraCastShadows( num );
1035 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1036 info->sampleSize = GetSurfaceExtraSampleSize( num );
1037 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1038 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1039 GetSurfaceExtraLightmapAxis( num, info->axis );
1042 if ( info->parentSurfaceNum >= 0 ) {
1043 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1046 /* determine surface bounds */
1047 ClearBounds( info->mins, info->maxs );
1048 for ( k = 0; k < ds->numVerts; k++ )
1050 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1051 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1054 /* find all the bsp clusters the surface falls into */
1055 for ( k = 0; k < numBSPLeafs; k++ )
1058 leaf = &bspLeafs[ k ];
1061 if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1062 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1063 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
1067 /* test leaf surfaces */
1068 for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1070 if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
1071 if ( numSurfaceClusters >= maxSurfaceClusters ) {
1072 Error( "maxSurfaceClusters exceeded" );
1074 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1075 numSurfaceClusters++;
1076 info->numSurfaceClusters++;
1081 /* determine if surface is planar */
1082 if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
1084 info->plane = safe_malloc( 4 * sizeof( float ) );
1085 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1086 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1089 /* determine if surface requires a lightmap */
1090 if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
1091 ds->surfaceType == MST_FOLIAGE ||
1092 ( info->si->compileFlags & C_VERTEXLIT ) ) {
1093 numSurfsVertexLit++;
1097 numSurfsLightmapped++;
1098 info->hasLightmap = qtrue;
1103 /* find longest map distance */
1104 VectorSubtract( mapMaxs, mapMins, mapSize );
1105 maxMapDistance = VectorLength( mapSize );
1107 /* sort the surfaces info list */
1108 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1110 /* allocate a list of surfaces that would go into raw lightmaps */
1111 numLightSurfaces = 0;
1112 lightSurfaces = safe_malloc0( numSurfsLightmapped * sizeof( int ) );
1114 /* allocate a list of raw lightmaps */
1115 numRawSuperLuxels = 0;
1116 numRawLightmaps = 0;
1117 rawLightmaps = safe_malloc0( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1119 /* walk the list of sorted surfaces */
1120 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1122 /* get info and attempt early out */
1123 num = sortSurfaces[ i ];
1124 ds = &bspDrawSurfaces[ num ];
1125 info = &surfaceInfos[ num ];
1126 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1130 /* allocate a new raw lightmap */
1131 lm = &rawLightmaps[ numRawLightmaps ];
1135 lm->splotchFix = info->si->splotchFix;
1136 lm->firstLightSurface = numLightSurfaces;
1137 lm->numLightSurfaces = 0;
1138 /* vortex: multiply lightmap sample size by -samplescale */
1139 if ( sampleScale > 0 ) {
1140 lm->sampleSize = info->sampleSize * sampleScale;
1143 lm->sampleSize = info->sampleSize;
1145 lm->actualSampleSize = lm->sampleSize;
1146 lm->entityNum = info->entityNum;
1147 lm->recvShadows = info->recvShadows;
1148 lm->brightness = info->si->lmBrightness;
1149 lm->filterRadius = info->si->lmFilterRadius;
1150 VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
1151 lm->floodlightDistance = info->si->floodlightDistance;
1152 lm->floodlightIntensity = info->si->floodlightIntensity;
1153 lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
1154 VectorCopy( info->axis, lm->axis );
1155 lm->plane = info->plane;
1156 VectorCopy( info->mins, lm->mins );
1157 VectorCopy( info->maxs, lm->maxs );
1159 lm->customWidth = info->si->lmCustomWidth;
1160 lm->customHeight = info->si->lmCustomHeight;
1162 /* add the surface to the raw lightmap */
1163 AddSurfaceToRawLightmap( num, lm );
1166 /* do an exhaustive merge */
1170 /* walk the list of surfaces again */
1172 for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1174 /* get info and attempt early out */
1175 num2 = sortSurfaces[ j ];
1176 info2 = &surfaceInfos[ num2 ];
1177 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1181 /* add the surface to the raw lightmap */
1182 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1189 lm->numLightSurfaces--;
1195 /* finish the lightmap and allocate the various buffers */
1196 FinishRawLightmap( lm );
1199 /* allocate vertex luxel storage */
1200 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1202 vertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1203 radVertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1206 /* emit some stats */
1207 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1208 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1209 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1210 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1211 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1212 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1213 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1214 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1220 StitchSurfaceLightmaps()
1221 stitches lightmap edges
1222 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1225 #define MAX_STITCH_CANDIDATES 32
1226 #define MAX_STITCH_LUXELS 64
1228 void StitchSurfaceLightmaps( void ){
1229 int i, j, x, y, x2, y2, *cluster, *cluster2,
1230 numStitched, numCandidates, numLuxels, f, fOld, start;
1231 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1232 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1233 sampleSize, average[ 3 ], totalColor, ootc;
1236 /* disabled for now */
1240 Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1244 start = I_FloatTime();
1246 /* walk the list of raw lightmaps */
1248 for ( i = 0; i < numRawLightmaps; i++ )
1250 /* print pacifier */
1251 f = 10 * i / numRawLightmaps;
1254 Sys_Printf( "%i...", f );
1257 /* get lightmap a */
1258 a = &rawLightmaps[ i ];
1260 /* walk rest of lightmaps */
1262 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1264 /* get lightmap b */
1265 b = &rawLightmaps[ j ];
1267 /* test bounding box */
1268 if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1269 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1270 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1275 c[ numCandidates++ ] = b;
1279 for ( y = 0; y < a->sh; y++ )
1281 for ( x = 0; x < a->sw; x++ )
1283 /* ignore unmapped/unlit luxels */
1285 cluster = SUPER_CLUSTER( x, y );
1286 if ( *cluster == CLUSTER_UNMAPPED ) {
1289 luxel = SUPER_LUXEL( 0, x, y );
1290 if ( luxel[ 3 ] <= 0.0f ) {
1294 /* get particulars */
1295 origin = SUPER_ORIGIN( x, y );
1296 normal = SUPER_NORMAL( x, y );
1298 /* walk candidate list */
1299 for ( j = 0; j < numCandidates; j++ )
1305 /* set samplesize to the smaller of the pair */
1306 sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1308 /* test bounding box */
1309 if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1310 origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1311 origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1315 /* walk candidate luxels */
1316 VectorClear( average );
1319 for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1321 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1323 /* ignore same luxels */
1324 if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1328 /* ignore unmapped/unlit luxels */
1329 cluster2 = SUPER_CLUSTER( x2, y2 );
1330 if ( *cluster2 == CLUSTER_UNMAPPED ) {
1333 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1334 if ( luxel2[ 3 ] <= 0.0f ) {
1338 /* get particulars */
1339 origin2 = SUPER_ORIGIN( x2, y2 );
1340 normal2 = SUPER_NORMAL( x2, y2 );
1343 if ( DotProduct( normal, normal2 ) < 0.5f ) {
1348 if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1349 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1350 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1355 //% VectorSet( luxel2, 255, 0, 255 );
1356 VectorAdd( average, luxel2, average );
1357 totalColor += luxel2[ 3 ];
1362 if ( numLuxels == 0 ) {
1367 ootc = 1.0f / totalColor;
1368 VectorScale( average, ootc, luxel );
1376 /* emit statistics */
1377 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1378 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1385 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1388 #define SOLID_EPSILON 0.0625
1389 #define LUXEL_TOLERANCE 0.0025
1390 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1392 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1395 double delta, total, rd, gd, bd;
1396 float *aLuxel, *bLuxel;
1399 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1400 if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1401 ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1406 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1407 a->brightness != b->brightness ||
1408 a->solid[ aNum ] != b->solid[ bNum ] ||
1409 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1413 /* compare solid color lightmaps */
1414 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1416 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1417 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1418 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1421 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1429 /* compare nonsolid lightmaps */
1430 if ( a->w != b->w || a->h != b->h ) {
1434 /* compare luxels */
1437 for ( y = 0; y < a->h; y++ )
1439 for ( x = 0; x < a->w; x++ )
1441 /* increment total */
1445 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1446 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1448 /* ignore unused luxels */
1449 if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1454 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1455 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1456 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1458 /* 2003-09-27: compare individual luxels */
1459 if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1463 /* compare (fixme: take into account perceptual differences) */
1464 delta += rd * LUXEL_COLOR_FRAC;
1465 delta += gd * LUXEL_COLOR_FRAC;
1466 delta += bd * LUXEL_COLOR_FRAC;
1468 /* is the change too high? */
1469 if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1475 /* made it this far, they must be identical (or close enough) */
1483 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1486 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1489 float luxel[ 3 ], *aLuxel, *bLuxel;
1493 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1494 a->brightness != b->brightness ||
1495 a->solid[ aNum ] != b->solid[ bNum ] ||
1496 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1500 /* compare solid lightmaps */
1501 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1503 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1504 VectorScale( luxel, 0.5f, luxel );
1507 VectorCopy( luxel, a->solidColor[ aNum ] );
1508 VectorCopy( luxel, b->solidColor[ bNum ] );
1510 /* return to sender */
1514 /* compare nonsolid lightmaps */
1515 if ( a->w != b->w || a->h != b->h ) {
1520 for ( y = 0; y < a->h; y++ )
1522 for ( x = 0; x < a->w; x++ )
1525 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1526 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1528 /* handle occlusion mismatch */
1529 if ( aLuxel[ 0 ] < 0.0f ) {
1530 VectorCopy( bLuxel, aLuxel );
1532 else if ( bLuxel[ 0 ] < 0.0f ) {
1533 VectorCopy( aLuxel, bLuxel );
1538 VectorAdd( aLuxel, bLuxel, luxel );
1539 VectorScale( luxel, 0.5f, luxel );
1541 /* debugging code */
1542 //% luxel[ 2 ] += 64.0f;
1545 VectorCopy( luxel, aLuxel );
1546 VectorCopy( luxel, bLuxel );
1559 determines if a single luxel is can be approximated with the interpolated vertex rgba
1562 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1563 int i, x, y, d, lightmapNum;
1565 vec3_t color, vertexColor;
1566 byte cb[ 4 ], vcb[ 4 ];
1569 /* find luxel xy coords */
1570 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1571 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1575 else if ( x >= lm->w ) {
1581 else if ( y >= lm->h ) {
1586 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1589 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1594 luxel = BSP_LUXEL( lightmapNum, x, y );
1596 /* ignore occluded luxels */
1597 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1601 /* copy, set min color and compare */
1602 VectorCopy( luxel, color );
1603 VectorCopy( dv->color[ 0 ], vertexColor );
1605 /* styles are not affected by minlight */
1606 if ( lightmapNum == 0 ) {
1607 for ( i = 0; i < 3; i++ )
1610 if ( color[ i ] < minLight[ i ] ) {
1611 color[ i ] = minLight[ i ];
1613 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1614 vertexColor[ i ] = minLight[ i ];
1620 ColorToBytes( color, cb, 1.0f );
1621 ColorToBytes( vertexColor, vcb, 1.0f );
1624 for ( i = 0; i < 3; i++ )
1626 d = cb[ i ] - vcb[ i ];
1630 if ( d > approximateTolerance ) {
1636 /* close enough for the girls i date */
1643 ApproximateTriangle()
1644 determines if a single triangle can be approximated with vertex rgba
1647 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1648 bspDrawVert_t mid, *dv2[ 3 ];
1652 /* approximate the vertexes */
1653 if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1656 if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1659 if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1663 /* subdivide calc */
1666 float dx, dy, dist, maxDist;
1669 /* find the longest edge and split it */
1672 for ( i = 0; i < 3; i++ )
1674 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1675 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1676 dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1677 if ( dist > maxDist ) {
1683 /* try to early out */
1684 if ( i < 0 || maxDist < subdivideThreshold ) {
1689 /* split the longest edge and map it */
1690 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1691 if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1695 /* recurse to first triangle */
1696 VectorCopy( dv, dv2 );
1698 if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1702 /* recurse to second triangle */
1703 VectorCopy( dv, dv2 );
1704 dv2[ ( max + 1 ) % 3 ] = ∣
1705 return ApproximateTriangle_r( lm, dv2 );
1711 ApproximateLightmap()
1712 determines if a raw lightmap can be approximated sufficiently with vertex colors
1715 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1716 int n, num, i, x, y, pw[ 5 ], r;
1717 bspDrawSurface_t *ds;
1718 surfaceInfo_t *info;
1719 mesh_t src, *subdivided, *mesh;
1720 bspDrawVert_t *verts, *dv[ 3 ];
1721 qboolean approximated;
1724 /* approximating? */
1725 if ( approximateTolerance <= 0 ) {
1729 /* test for jmonroe */
1731 /* don't approx lightmaps with styled twins */
1732 if ( lm->numStyledTwins > 0 ) {
1736 /* don't approx lightmaps with styles */
1737 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1739 if ( lm->styles[ i ] != LS_NONE ) {
1745 /* assume reduced until shadow detail is found */
1746 approximated = qtrue;
1748 /* walk the list of surfaces on this raw lightmap */
1749 for ( n = 0; n < lm->numLightSurfaces; n++ )
1752 num = lightSurfaces[ lm->firstLightSurface + n ];
1753 ds = &bspDrawSurfaces[ num ];
1754 info = &surfaceInfos[ num ];
1756 /* assume not-reduced initially */
1757 info->approximated = qfalse;
1759 /* bail if lightmap doesn't match up */
1760 if ( info->lm != lm ) {
1764 /* bail if not vertex lit */
1765 if ( info->si->noVertexLight ) {
1769 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1770 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1771 ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1772 ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1773 info->approximated = qtrue;
1774 numSurfsVertexForced++;
1778 /* handle the triangles */
1779 switch ( ds->surfaceType )
1783 verts = yDrawVerts + ds->firstVert;
1785 /* map the triangles */
1786 info->approximated = qtrue;
1787 for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1789 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1790 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1791 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1792 info->approximated = ApproximateTriangle_r( lm, dv );
1797 /* make a mesh from the drawsurf */
1798 src.width = ds->patchWidth;
1799 src.height = ds->patchHeight;
1800 src.verts = &yDrawVerts[ ds->firstVert ];
1801 //% subdivided = SubdivideMesh( src, 8, 512 );
1802 subdivided = SubdivideMesh2( src, info->patchIterations );
1804 /* fit it to the curve and remove colinear verts on rows/columns */
1805 PutMeshOnCurve( *subdivided );
1806 mesh = RemoveLinearMeshColumnsRows( subdivided );
1807 FreeMesh( subdivided );
1810 verts = mesh->verts;
1812 /* map the mesh quads */
1813 info->approximated = qtrue;
1814 for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1816 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1819 pw[ 0 ] = x + ( y * mesh->width );
1820 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1821 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1822 pw[ 3 ] = x + 1 + ( y * mesh->width );
1823 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1828 /* get drawverts and map first triangle */
1829 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1830 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1831 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1832 info->approximated = ApproximateTriangle_r( lm, dv );
1834 /* get drawverts and map second triangle */
1835 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1836 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1837 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1838 if ( info->approximated ) {
1839 info->approximated = ApproximateTriangle_r( lm, dv );
1853 if ( info->approximated == qfalse ) {
1854 approximated = qfalse;
1857 numSurfsVertexApproximated++;
1862 return approximated;
1868 TestOutLightmapStamp()
1869 tests a stamp on a given lightmap for validity
1872 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1873 int sx, sy, ox, oy, offset;
1878 if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1882 /* solid lightmaps test a 1x1 stamp */
1883 if ( lm->solid[ lightmapNum ] ) {
1884 offset = ( y * olm->customWidth ) + x;
1885 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1891 /* test the stamp */
1892 for ( sy = 0; sy < lm->h; sy++ )
1894 for ( sx = 0; sx < lm->w; sx++ )
1897 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1898 if ( luxel[ 0 ] < 0.0f ) {
1902 /* get bsp lightmap coords and test */
1905 offset = ( oy * olm->customWidth ) + ox;
1906 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1912 /* stamp is empty */
1920 sets up an output lightmap
1923 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1925 if ( lm == NULL || olm == NULL ) {
1929 /* is this a "normal" bsp-stored lightmap? */
1930 if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1931 olm->lightmapNum = numBSPLightmaps;
1934 /* lightmaps are interleaved with light direction maps */
1940 olm->lightmapNum = -3;
1943 /* set external lightmap number */
1944 olm->extLightmapNum = -1;
1947 olm->numLightmaps = 0;
1948 olm->customWidth = lm->customWidth;
1949 olm->customHeight = lm->customHeight;
1950 olm->freeLuxels = olm->customWidth * olm->customHeight;
1951 olm->numShaders = 0;
1953 /* allocate buffers */
1954 olm->lightBits = safe_malloc0( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1955 olm->bspLightBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
1957 olm->bspDirBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
1965 for a given surface lightmap, find output lightmap pages and positions for it
1968 #define LIGHTMAP_RESERVE_COUNT 1
1969 static void FindOutLightmaps( rawLightmap_t *lm, qboolean fastLightmapSearch ){
1970 int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
1972 surfaceInfo_t *info;
1973 float *luxel, *deluxel;
1974 vec3_t color, direction;
1977 int xIncrement, yIncrement;
1979 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1980 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1981 lm->outLightmapNums[ lightmapNum ] = -3;
1983 /* can this lightmap be approximated with vertex color? */
1984 if ( ApproximateLightmap( lm ) ) {
1989 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1992 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1996 /* don't store twinned lightmaps */
1997 if ( lm->twins[ lightmapNum ] != NULL ) {
2001 /* if this is a styled lightmap, try some normalized locations first */
2003 if ( lightmapNum > 0 && outLightmaps != NULL ) {
2005 for ( j = 0; j < 2; j++ )
2007 /* try identical position */
2008 for ( i = 0; i < numOutLightmaps; i++ )
2010 /* get the output lightmap */
2011 olm = &outLightmaps[ i ];
2013 /* simple early out test */
2014 if ( olm->freeLuxels < lm->used ) {
2018 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2019 if ( olm->customWidth != lm->customWidth ||
2020 olm->customHeight != lm->customHeight ) {
2026 x = lm->lightmapX[ 0 ];
2027 y = lm->lightmapY[ 0 ];
2028 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2034 for ( sy = -1; sy <= 1; sy++ )
2036 for ( sx = -1; sx <= 1; sx++ )
2038 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
2039 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
2040 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2064 /* try normal placement algorithm */
2065 if ( ok == qfalse ) {
2070 /* walk the list of lightmap pages */
2071 if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
2075 i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
2077 for ( ; i < numOutLightmaps; i++ )
2079 /* get the output lightmap */
2080 olm = &outLightmaps[ i ];
2082 /* simple early out test */
2083 if ( olm->freeLuxels < lm->used ) {
2087 /* if fast allocation, skip lightmap files that are more than 90% complete */
2088 if ( fastLightmapSearch == qtrue ) {
2089 if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
2094 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2095 if ( olm->customWidth != lm->customWidth ||
2096 olm->customHeight != lm->customHeight ) {
2101 if ( lm->solid[ lightmapNum ] ) {
2102 xMax = olm->customWidth;
2103 yMax = olm->customHeight;
2107 xMax = ( olm->customWidth - lm->w ) + 1;
2108 yMax = ( olm->customHeight - lm->h ) + 1;
2111 /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
2112 if ( fastLightmapSearch == qtrue ) {
2113 xIncrement = MAX(1, lm->w / 15);
2114 yIncrement = MAX(1, lm->h / 15);
2121 /* walk the origin around the lightmap */
2122 for ( y = 0; y < yMax; y += yIncrement )
2124 for ( x = 0; x < xMax; x += xIncrement )
2126 /* find a fine tract of lauhnd */
2127 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2150 if ( ok == qfalse ) {
2151 /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2152 numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2153 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2154 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2155 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2156 free( outLightmaps );
2160 /* initialize both out lightmaps */
2161 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2162 SetupOutLightmap( lm, &outLightmaps[ k ] );
2164 /* set out lightmap */
2165 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2166 olm = &outLightmaps[ i ];
2168 /* set stamp xy origin to the first surface lightmap */
2169 if ( lightmapNum > 0 ) {
2170 x = lm->lightmapX[ 0 ];
2171 y = lm->lightmapY[ 0 ];
2175 /* if this is a style-using lightmap, it must be exported */
2176 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2177 olm->extLightmapNum = 0;
2180 /* add the surface lightmap to the bsp lightmap */
2181 lm->outLightmapNums[ lightmapNum ] = i;
2182 lm->lightmapX[ lightmapNum ] = x;
2183 lm->lightmapY[ lightmapNum ] = y;
2184 olm->numLightmaps++;
2187 for ( i = 0; i < lm->numLightSurfaces; i++ )
2189 /* get surface info */
2190 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2192 /* test for shader */
2193 for ( j = 0; j < olm->numShaders; j++ )
2195 if ( olm->shaders[ j ] == info->si ) {
2200 /* if it doesn't exist, add it */
2201 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2202 olm->shaders[ olm->numShaders ] = info->si;
2204 numLightmapShaders++;
2209 if ( lm->solid[ lightmapNum ] ) {
2219 /* mark the bits used */
2220 for ( y = 0; y < yMax; y++ )
2222 for ( x = 0; x < xMax; x++ )
2225 luxel = BSP_LUXEL( lightmapNum, x, y );
2226 deluxel = BSP_DELUXEL( x, y );
2227 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2231 /* set minimum light */
2232 if ( lm->solid[ lightmapNum ] ) {
2234 VectorSet( color, 255.0f, 0.0f, 0.0f );
2237 VectorCopy( lm->solidColor[ lightmapNum ], color );
2241 VectorCopy( luxel, color );
2244 /* styles are not affected by minlight */
2245 if ( lightmapNum == 0 ) {
2246 for ( i = 0; i < 3; i++ )
2248 if ( color[ i ] < minLight[ i ] ) {
2249 color[ i ] = minLight[ i ];
2254 /* get bsp lightmap coords */
2255 ox = x + lm->lightmapX[ lightmapNum ];
2256 oy = y + lm->lightmapY[ lightmapNum ];
2257 offset = ( oy * olm->customWidth ) + ox;
2259 /* flag pixel as used */
2260 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2264 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2265 ColorToBytes( color, pixel, lm->brightness );
2267 /* store direction */
2269 /* normalize average light direction */
2270 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2271 VectorScale( deluxel, 1000.0f, direction );
2272 VectorNormalize( direction, direction );
2273 VectorScale( direction, 127.5f, direction );
2274 for ( i = 0; i < 3; i++ )
2275 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2285 CompareRawLightmap()
2286 compare function for qsort()
2289 static int CompareRawLightmap( const void *a, const void *b ){
2290 rawLightmap_t *alm, *blm;
2291 surfaceInfo_t *aInfo, *bInfo;
2296 alm = &rawLightmaps[ *( (const int*) a ) ];
2297 blm = &rawLightmaps[ *( (const int*) b ) ];
2299 /* get min number of surfaces */
2300 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2303 for ( i = 0; i < min; i++ )
2305 /* get surface info */
2306 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2307 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2309 /* compare shader names */
2310 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2316 /* test style count */
2318 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2319 diff += blm->styles[ i ] - alm->styles[ i ];
2325 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2330 /* must be equivalent */
2336 void FillOutLightmap( outLightmap_t *olm ){
2339 vec3_t dir_sum, light_sum;
2341 byte *lightBitsNew = NULL;
2342 byte *lightBytesNew = NULL;
2343 byte *dirBytesNew = NULL;
2345 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2346 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2348 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2352 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2353 olm->lightBits[0] |= 1;
2354 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2355 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2356 olm->bspLightBytes[0] = 255;
2357 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2360 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2361 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2363 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2369 for ( y = 0; y < olm->customHeight; ++y )
2371 for ( x = 0; x < olm->customWidth; ++x )
2373 ofs = y * olm->customWidth + x;
2374 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2378 VectorClear( dir_sum );
2379 VectorClear( light_sum );
2381 /* try all four neighbors */
2382 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2383 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2385 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2387 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2391 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2392 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2394 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2396 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2400 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2401 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2403 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2405 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2409 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2410 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2412 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2414 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2420 ofs = y * olm->customWidth + x;
2421 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2422 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2424 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2434 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2435 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2437 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2441 free( lightBitsNew );
2442 free( lightBytesNew );
2444 free( dirBytesNew );
2451 StoreSurfaceLightmaps()
2452 stores the surface lightmaps into the bsp as byte rgb triplets
2455 void StoreSurfaceLightmaps( qboolean fastLightmapSearch, qboolean storeForReal ){
2456 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2457 int style, size, lightmapNum, lightmapNum2;
2458 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2459 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2460 float *deluxel, *bspDeluxel, *bspDeluxel2;
2462 int numUsed, numTwins, numTwinLuxels, numStored;
2463 float lmx, lmy, efficiency;
2465 bspDrawSurface_t *ds, *parent, dsTemp;
2466 surfaceInfo_t *info;
2467 rawLightmap_t *lm, *lm2;
2469 bspDrawVert_t *dv, *ydv, *dvParent;
2470 char dirname[ 1024 ], filename[ 1024 ];
2472 char lightmapName[ 128 ];
2473 const char *rgbGenValues[ 256 ];
2474 const char *alphaGenValues[ 256 ];
2478 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2481 if ( lmCustomDir ) {
2482 strcpy( dirname, lmCustomDir );
2486 strcpy( dirname, source );
2487 StripExtension( dirname );
2489 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2490 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2492 /* -----------------------------------------------------------------
2493 average the sampled luxels into the bsp luxels
2494 ----------------------------------------------------------------- */
2497 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2499 /* walk the list of raw lightmaps */
2503 numSolidLightmaps = 0;
2504 for ( i = 0; i < numRawLightmaps; i++ )
2507 lm = &rawLightmaps[ i ];
2509 /* walk individual lightmaps */
2510 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2513 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2517 /* allocate bsp luxel storage */
2518 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2519 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2520 lm->bspLuxels[ lightmapNum ] = safe_malloc0( size );
2523 /* allocate radiosity lightmap storage */
2525 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2526 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2527 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2529 memset( lm->radLuxels[ lightmapNum ], 0, size );
2532 /* average supersampled luxels */
2533 for ( y = 0; y < lm->h; y++ )
2535 for ( x = 0; x < lm->w; x++ )
2539 occludedSamples = 0.0f;
2541 VectorClear( sample );
2542 VectorClear( occludedSample );
2543 VectorClear( dirSample );
2544 for ( ly = 0; ly < superSample; ly++ )
2546 for ( lx = 0; lx < superSample; lx++ )
2549 sx = x * superSample + lx;
2550 sy = y * superSample + ly;
2551 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2552 deluxel = SUPER_DELUXEL( sx, sy );
2553 normal = SUPER_NORMAL( sx, sy );
2554 cluster = SUPER_CLUSTER( sx, sy );
2556 /* sample deluxemap */
2557 if ( deluxemap && lightmapNum == 0 ) {
2558 VectorAdd( dirSample, deluxel, dirSample );
2561 /* keep track of used/occluded samples */
2562 if ( *cluster != CLUSTER_UNMAPPED ) {
2566 /* handle lightmap border? */
2567 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2568 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2573 else if ( debug && *cluster < 0 ) {
2574 if ( *cluster == CLUSTER_UNMAPPED ) {
2575 VectorSet( luxel, 255, 204, 0 );
2577 else if ( *cluster == CLUSTER_OCCLUDED ) {
2578 VectorSet( luxel, 255, 0, 255 );
2580 else if ( *cluster == CLUSTER_FLOODED ) {
2581 VectorSet( luxel, 0, 32, 255 );
2583 VectorAdd( occludedSample, luxel, occludedSample );
2584 occludedSamples += 1.0f;
2587 /* normal luxel handling */
2588 else if ( luxel[ 3 ] > 0.0f ) {
2589 /* handle lit or flooded luxels */
2590 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2591 VectorAdd( sample, luxel, sample );
2592 samples += luxel[ 3 ];
2595 /* handle occluded or unmapped luxels */
2598 VectorAdd( occludedSample, luxel, occludedSample );
2599 occludedSamples += luxel[ 3 ];
2602 /* handle style debugging */
2603 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2604 VectorCopy( debugColors[ 0 ], sample );
2611 /* only use occluded samples if necessary */
2612 if ( samples <= 0.0f ) {
2613 VectorCopy( occludedSample, sample );
2614 samples = occludedSamples;
2618 luxel = SUPER_LUXEL( lightmapNum, x, y );
2619 deluxel = SUPER_DELUXEL( x, y );
2621 /* store light direction */
2622 if ( deluxemap && lightmapNum == 0 ) {
2623 VectorCopy( dirSample, deluxel );
2626 /* store the sample back in super luxels */
2627 if ( samples > 0.01f ) {
2628 VectorScale( sample, ( 1.0f / samples ), luxel );
2632 /* if any samples were mapped in any way, store ambient color */
2633 else if ( mappedSamples > 0 ) {
2634 if ( lightmapNum == 0 ) {
2635 VectorCopy( ambientColor, luxel );
2638 VectorClear( luxel );
2643 /* store a bogus value to be fixed later */
2646 VectorClear( luxel );
2654 ClearBounds( colorMins, colorMaxs );
2656 /* clean up and store into bsp luxels */
2657 for ( y = 0; y < lm->h; y++ )
2659 for ( x = 0; x < lm->w; x++ )
2662 luxel = SUPER_LUXEL( lightmapNum, x, y );
2663 deluxel = SUPER_DELUXEL( x, y );
2665 /* copy light direction */
2666 if ( deluxemap && lightmapNum == 0 ) {
2667 VectorCopy( deluxel, dirSample );
2670 /* is this a valid sample? */
2671 if ( luxel[ 3 ] > 0.0f ) {
2672 VectorCopy( luxel, sample );
2673 samples = luxel[ 3 ];
2677 /* fix negative samples */
2678 for ( j = 0; j < 3; j++ )
2680 if ( sample[ j ] < 0.0f ) {
2687 /* nick an average value from the neighbors */
2688 VectorClear( sample );
2689 VectorClear( dirSample );
2692 /* fixme: why is this disabled?? */
2693 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2695 if ( sy < 0 || sy >= lm->h ) {
2699 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2701 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2705 /* get neighbor's particulars */
2706 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2707 if ( luxel[ 3 ] < 0.0f ) {
2710 VectorAdd( sample, luxel, sample );
2711 samples += luxel[ 3 ];
2716 if ( samples == 0.0f ) {
2717 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2725 /* fix negative samples */
2726 for ( j = 0; j < 3; j++ )
2728 if ( sample[ j ] < 0.0f ) {
2735 /* scale the sample */
2736 VectorScale( sample, ( 1.0f / samples ), sample );
2738 /* store the sample in the radiosity luxels */
2740 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2741 VectorCopy( sample, radLuxel );
2743 /* if only storing bounced light, early out here */
2744 if ( bounceOnly && !bouncing ) {
2749 /* store the sample in the bsp luxels */
2750 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2751 bspDeluxel = BSP_DELUXEL( x, y );
2753 VectorAdd( bspLuxel, sample, bspLuxel );
2754 if ( deluxemap && lightmapNum == 0 ) {
2755 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2758 /* add color to bounds for solid checking */
2759 if ( samples > 0.0f ) {
2760 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2765 /* set solid color */
2766 lm->solid[ lightmapNum ] = qfalse;
2767 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2768 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2770 /* nocollapse prevents solid lightmaps */
2771 if ( noCollapse == qfalse ) {
2772 /* check solid color */
2773 VectorSubtract( colorMaxs, colorMins, sample );
2774 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2775 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2777 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2778 lm->solid[ lightmapNum ] = qtrue;
2779 numSolidLightmaps++;
2782 /* if all lightmaps aren't solid, then none of them are solid */
2783 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2784 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2786 if ( lm->solid[ y ] ) {
2787 numSolidLightmaps--;
2789 lm->solid[ y ] = qfalse;
2794 /* wrap bsp luxels if necessary */
2795 if ( lm->wrap[ 0 ] ) {
2796 for ( y = 0; y < lm->h; y++ )
2798 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2799 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2800 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2801 VectorScale( bspLuxel, 0.5f, bspLuxel );
2802 VectorCopy( bspLuxel, bspLuxel2 );
2803 if ( deluxemap && lightmapNum == 0 ) {
2804 bspDeluxel = BSP_DELUXEL( 0, y );
2805 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2806 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2807 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2808 VectorCopy( bspDeluxel, bspDeluxel2 );
2812 if ( lm->wrap[ 1 ] ) {
2813 for ( x = 0; x < lm->w; x++ )
2815 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2816 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2817 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2818 VectorScale( bspLuxel, 0.5f, bspLuxel );
2819 VectorCopy( bspLuxel, bspLuxel2 );
2820 if ( deluxemap && lightmapNum == 0 ) {
2821 bspDeluxel = BSP_DELUXEL( x, 0 );
2822 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2823 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2824 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2825 VectorCopy( bspDeluxel, bspDeluxel2 );
2832 /* -----------------------------------------------------------------
2833 convert modelspace deluxemaps to tangentspace
2834 ----------------------------------------------------------------- */
2837 if ( deluxemap && deluxemode == 1 ) {
2838 vec3_t worldUp, myNormal, myTangent, myBinormal;
2841 Sys_Printf( "converting..." );
2843 for ( i = 0; i < numRawLightmaps; i++ )
2846 lm = &rawLightmaps[ i ];
2848 /* walk lightmap samples */
2849 for ( y = 0; y < lm->sh; y++ )
2851 for ( x = 0; x < lm->sw; x++ )
2853 /* get normal and deluxel */
2854 normal = SUPER_NORMAL( x, y );
2855 cluster = SUPER_CLUSTER( x, y );
2856 bspDeluxel = BSP_DELUXEL( x, y );
2857 deluxel = SUPER_DELUXEL( x, y );
2860 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2862 /* get tangent vectors */
2863 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2864 if ( myNormal[ 2 ] == 1.0f ) {
2865 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2866 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2868 else if ( myNormal[ 2 ] == -1.0f ) {
2869 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2870 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2875 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2876 CrossProduct( myNormal, worldUp, myTangent );
2877 VectorNormalize( myTangent, myTangent );
2878 CrossProduct( myTangent, myNormal, myBinormal );
2879 VectorNormalize( myBinormal, myBinormal );
2882 /* project onto plane */
2883 dist = -DotProduct( myTangent, myNormal );
2884 VectorMA( myTangent, dist, myNormal, myTangent );
2885 dist = -DotProduct( myBinormal, myNormal );
2886 VectorMA( myBinormal, dist, myNormal, myBinormal );
2889 VectorNormalize( myTangent, myTangent );
2890 VectorNormalize( myBinormal, myBinormal );
2892 /* convert modelspace deluxel to tangentspace */
2893 dirSample[0] = bspDeluxel[0];
2894 dirSample[1] = bspDeluxel[1];
2895 dirSample[2] = bspDeluxel[2];
2896 VectorNormalize( dirSample, dirSample );
2898 /* fix tangents to world matrix */
2899 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2900 VectorNegate( myTangent, myTangent );
2903 /* build tangentspace vectors */
2904 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2905 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2906 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2913 /* -----------------------------------------------------------------
2915 ----------------------------------------------------------------- */
2917 #ifdef sdfsdfwq312323
2919 Sys_Printf( "blending..." );
2921 for ( i = 0; i < numRawLightmaps; i++ )
2927 lm = &rawLightmaps[ i ];
2929 /* walk individual lightmaps */
2930 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2933 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2937 /* walk lightmap samples */
2938 for ( y = 0; y < lm->sh; y++ )
2940 for ( x = 0; x < lm->sw; x++ )
2943 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2946 VectorNormalize( bspLuxel, myColor );
2947 myBrightness = VectorLength( bspLuxel );
2948 myBrightness *= ( 1 / 127.0f );
2949 myBrightness = myBrightness * myBrightness;
2950 myBrightness *= 127.0f;
2951 VectorScale( myColor, myBrightness, bspLuxel );
2958 /* -----------------------------------------------------------------
2959 collapse non-unique lightmaps
2960 ----------------------------------------------------------------- */
2962 if ( storeForReal && noCollapse == qfalse && deluxemap == qfalse ) {
2964 Sys_FPrintf( SYS_VRB, "collapsing..." );
2966 /* set all twin refs to null */
2967 for ( i = 0; i < numRawLightmaps; i++ )
2969 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2971 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2972 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2973 rawLightmaps[ i ].numStyledTwins = 0;
2977 /* walk the list of raw lightmaps */
2978 for ( i = 0; i < numRawLightmaps; i++ )
2981 lm = &rawLightmaps[ i ];
2983 /* walk lightmaps */
2984 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2987 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
2988 lm->twins[ lightmapNum ] != NULL ) {
2992 /* find all lightmaps that are virtually identical to this one */
2993 for ( j = i + 1; j < numRawLightmaps; j++ )
2996 lm2 = &rawLightmaps[ j ];
2998 /* walk lightmaps */
2999 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3002 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3003 lm2->twins[ lightmapNum2 ] != NULL ) {
3008 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3009 /* merge and set twin */
3010 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3011 lm2->twins[ lightmapNum2 ] = lm;
3012 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3014 numTwinLuxels += ( lm->w * lm->h );
3016 /* count styled twins */
3017 if ( lightmapNum > 0 ) {
3018 lm->numStyledTwins++;
3028 /* -----------------------------------------------------------------
3029 sort raw lightmaps by shader
3030 ----------------------------------------------------------------- */
3033 Sys_FPrintf( SYS_VRB, "sorting..." );
3035 /* allocate a new sorted list */
3036 if ( sortLightmaps == NULL ) {
3037 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3040 /* fill it out and sort it */
3041 for ( i = 0; i < numRawLightmaps; i++ )
3042 sortLightmaps[ i ] = i;
3043 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3045 /* -----------------------------------------------------------------
3046 allocate output lightmaps
3047 ----------------------------------------------------------------- */
3049 if ( storeForReal ) {
3051 Sys_FPrintf( SYS_VRB, "allocating..." );
3053 /* kill all existing output lightmaps */
3054 if ( outLightmaps != NULL ) {
3055 for ( i = 0; i < numOutLightmaps; i++ )
3057 free( outLightmaps[ i ].lightBits );
3058 free( outLightmaps[ i ].bspLightBytes );
3060 free( outLightmaps );
3061 outLightmaps = NULL;
3064 numLightmapShaders = 0;
3065 numOutLightmaps = 0;
3066 numBSPLightmaps = 0;
3067 numExtLightmaps = 0;
3069 /* find output lightmap */
3070 for ( i = 0; i < numRawLightmaps; i++ )
3072 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3073 FindOutLightmaps( lm, fastLightmapSearch );
3076 /* set output numbers in twinned lightmaps */
3077 for ( i = 0; i < numRawLightmaps; i++ )
3080 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3082 /* walk lightmaps */
3083 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3086 lm2 = lm->twins[ lightmapNum ];
3087 if ( lm2 == NULL ) {
3090 lightmapNum2 = lm->twinNums[ lightmapNum ];
3092 /* find output lightmap from twin */
3093 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3094 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3095 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3100 /* -----------------------------------------------------------------
3101 store output lightmaps
3102 ----------------------------------------------------------------- */
3104 if ( storeForReal ) {
3106 Sys_FPrintf( SYS_VRB, "storing..." );
3108 /* count the bsp lightmaps and allocate space */
3109 if ( bspLightBytes != NULL ) {
3110 free( bspLightBytes );
3112 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3113 numBSPLightBytes = 0;
3114 bspLightBytes = NULL;
3118 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3119 bspLightBytes = safe_malloc0( numBSPLightBytes );
3122 /* walk the list of output lightmaps */
3123 for ( i = 0; i < numOutLightmaps; i++ )
3125 /* get output lightmap */
3126 olm = &outLightmaps[ i ];
3128 /* fill output lightmap */
3129 if ( lightmapFill ) {
3130 FillOutLightmap( olm );
3133 /* is this a valid bsp lightmap? */
3134 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3135 /* copy lighting data */
3136 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3137 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3139 /* copy direction data */
3141 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3142 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3146 /* external lightmap? */
3147 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3148 /* make a directory for the lightmaps */
3151 /* set external lightmap number */
3152 olm->extLightmapNum = numExtLightmaps;
3154 /* write lightmap */
3155 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3156 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3157 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3160 /* write deluxemap */
3162 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3163 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3164 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3167 if ( debugDeluxemap ) {
3168 olm->extLightmapNum++;
3174 if ( numExtLightmaps > 0 ) {
3175 Sys_FPrintf( SYS_VRB, "\n" );
3178 /* delete unused external lightmaps */
3179 for ( i = numExtLightmaps; i; i++ )
3181 /* determine if file exists */
3182 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3183 if ( !FileExists( filename ) ) {
3192 /* -----------------------------------------------------------------
3193 project the lightmaps onto the bsp surfaces
3194 ----------------------------------------------------------------- */
3196 if ( storeForReal ) {
3198 Sys_FPrintf( SYS_VRB, "projecting..." );
3200 /* walk the list of surfaces */
3201 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3203 /* get the surface and info */
3204 ds = &bspDrawSurfaces[ i ];
3205 info = &surfaceInfos[ i ];
3209 /* handle surfaces with identical parent */
3210 if ( info->parentSurfaceNum >= 0 ) {
3211 /* preserve original data and get parent */
3212 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3213 memcpy( &dsTemp, ds, sizeof( *ds ) );
3215 /* overwrite child with parent data */
3216 memcpy( ds, parent, sizeof( *ds ) );
3218 /* restore key parts */
3219 ds->fogNum = dsTemp.fogNum;
3220 ds->firstVert = dsTemp.firstVert;
3221 ds->firstIndex = dsTemp.firstIndex;
3222 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3224 /* set vertex data */
3225 dv = &bspDrawVerts[ ds->firstVert ];
3226 dvParent = &bspDrawVerts[ parent->firstVert ];
3227 for ( j = 0; j < ds->numVerts; j++ )
3229 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3230 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3237 /* handle vertex lit or approximated surfaces */
3238 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3239 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3241 ds->lightmapNum[ lightmapNum ] = -3;
3242 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3246 /* handle lightmapped surfaces */
3249 /* walk lightmaps */
3250 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3253 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3255 /* handle unused style */
3256 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3257 ds->lightmapNum[ lightmapNum ] = -3;
3261 /* get output lightmap */
3262 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3264 /* set bsp lightmap number */
3265 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3267 /* deluxemap debugging makes the deluxemap visible */
3268 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3269 ds->lightmapNum[ lightmapNum ]++;
3272 /* calc lightmap origin in texture space */
3273 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3274 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3276 /* calc lightmap st coords */
3277 dv = &bspDrawVerts[ ds->firstVert ];
3278 ydv = &yDrawVerts[ ds->firstVert ];
3279 for ( j = 0; j < ds->numVerts; j++ )
3281 if ( lm->solid[ lightmapNum ] ) {
3282 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3283 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3287 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3288 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3294 /* store vertex colors */
3295 dv = &bspDrawVerts[ ds->firstVert ];
3296 for ( j = 0; j < ds->numVerts; j++ )
3298 /* walk lightmaps */
3299 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3301 /* handle unused style */
3302 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3303 VectorClear( color );
3307 /* get vertex color */
3308 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3309 VectorCopy( luxel, color );
3311 /* set minimum light */
3312 if ( lightmapNum == 0 ) {
3313 for ( k = 0; k < 3; k++ )
3314 if ( color[ k ] < minVertexLight[ k ] ) {
3315 color[ k ] = minVertexLight[ k ];
3320 /* store to bytes */
3321 if ( !info->si->noVertexLight ) {
3322 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3327 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3328 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3330 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3334 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3335 dv = &bspDrawVerts[ ds->firstVert ];
3337 /* depthFunc equal? */
3338 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3345 /* generate stages for styled lightmaps */
3346 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3349 style = lm->styles[ lightmapNum ];
3350 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3354 /* get output lightmap */
3355 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3358 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3359 strcpy( lightmapName, "$lightmap" );
3362 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3365 /* get rgbgen string */
3366 if ( rgbGenValues[ style ] == NULL ) {
3367 sprintf( key, "_style%drgbgen", style );
3368 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3369 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3370 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3374 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3375 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3381 /* get alphagen string */
3382 if ( alphaGenValues[ style ] == NULL ) {
3383 sprintf( key, "_style%dalphagen", style );
3384 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3386 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3387 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3390 alphaGen[ 0 ] = '\0';
3393 /* calculate st offset */
3394 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3395 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3397 /* create additional stage */
3398 if ( lmx == 0.0f && lmy == 0.0f ) {
3399 sprintf( styleStage, "\t{\n"
3400 "\t\tmap %s\n" /* lightmap */
3401 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3402 "%s" /* depthFunc equal */
3405 "\t\ttcGen lightmap\n"
3408 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3414 sprintf( styleStage, "\t{\n"
3415 "\t\tmap %s\n" /* lightmap */
3416 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3417 "%s" /* depthFunc equal */
3420 "\t\ttcGen lightmap\n"
3421 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3424 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3432 strcat( styleStages, styleStage );
3435 /* create custom shader */
3436 if ( info->si->styleMarker == 2 ) {
3437 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3440 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3443 /* emit remap command */
3444 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3447 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3448 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3449 //% Sys_Printf( ")\n" );
3452 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3453 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3454 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3455 /* get output lightmap */
3456 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3458 /* do some name mangling */
3459 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3461 /* create custom shader */
3462 csi = CustomShader( info->si, "$lightmap", lightmapName );
3465 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3466 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3467 //% Sys_Printf( ")\n" );
3470 /* use the normal plain-jane shader */
3472 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3478 Sys_FPrintf( SYS_VRB, "done.\n" );
3480 /* calc num stored */
3481 numStored = numBSPLightBytes / 3;
3482 efficiency = ( numStored <= 0 )
3484 : (float) numUsed / (float) numStored;
3486 if ( storeForReal ) {
3488 Sys_Printf( "%9d luxels used\n", numUsed );
3489 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3490 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3491 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3492 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3493 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3494 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3495 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3496 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3498 /* write map shader file */
3499 WriteMapShaderFile();