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 ) ||
1094 numSurfsVertexLit++;
1098 numSurfsLightmapped++;
1099 info->hasLightmap = qtrue;
1104 /* find longest map distance */
1105 VectorSubtract( mapMaxs, mapMins, mapSize );
1106 maxMapDistance = VectorLength( mapSize );
1108 /* sort the surfaces info list */
1109 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1111 /* allocate a list of surfaces that would go into raw lightmaps */
1112 numLightSurfaces = 0;
1113 lightSurfaces = safe_malloc0( numSurfsLightmapped * sizeof( int ) );
1115 /* allocate a list of raw lightmaps */
1116 numRawSuperLuxels = 0;
1117 numRawLightmaps = 0;
1118 rawLightmaps = safe_malloc0( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1120 /* walk the list of sorted surfaces */
1121 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1123 /* get info and attempt early out */
1124 num = sortSurfaces[ i ];
1125 ds = &bspDrawSurfaces[ num ];
1126 info = &surfaceInfos[ num ];
1127 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1131 /* allocate a new raw lightmap */
1132 lm = &rawLightmaps[ numRawLightmaps ];
1136 lm->splotchFix = info->si->splotchFix;
1137 lm->firstLightSurface = numLightSurfaces;
1138 lm->numLightSurfaces = 0;
1139 /* vortex: multiply lightmap sample size by -samplescale */
1140 if ( sampleScale > 0 ) {
1141 lm->sampleSize = info->sampleSize * sampleScale;
1144 lm->sampleSize = info->sampleSize;
1146 lm->actualSampleSize = lm->sampleSize;
1147 lm->entityNum = info->entityNum;
1148 lm->recvShadows = info->recvShadows;
1149 lm->brightness = info->si->lmBrightness;
1150 lm->filterRadius = info->si->lmFilterRadius;
1151 VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
1152 lm->floodlightDistance = info->si->floodlightDistance;
1153 lm->floodlightIntensity = info->si->floodlightIntensity;
1154 lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
1155 VectorCopy( info->axis, lm->axis );
1156 lm->plane = info->plane;
1157 VectorCopy( info->mins, lm->mins );
1158 VectorCopy( info->maxs, lm->maxs );
1160 lm->customWidth = info->si->lmCustomWidth;
1161 lm->customHeight = info->si->lmCustomHeight;
1163 /* add the surface to the raw lightmap */
1164 AddSurfaceToRawLightmap( num, lm );
1167 /* do an exhaustive merge */
1171 /* walk the list of surfaces again */
1173 for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1175 /* get info and attempt early out */
1176 num2 = sortSurfaces[ j ];
1177 info2 = &surfaceInfos[ num2 ];
1178 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1182 /* add the surface to the raw lightmap */
1183 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1190 lm->numLightSurfaces--;
1196 /* finish the lightmap and allocate the various buffers */
1197 FinishRawLightmap( lm );
1200 /* allocate vertex luxel storage */
1201 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1203 vertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1204 radVertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1207 /* emit some stats */
1208 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1209 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1210 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1211 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1212 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1213 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1214 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1215 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1221 StitchSurfaceLightmaps()
1222 stitches lightmap edges
1223 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1226 #define MAX_STITCH_CANDIDATES 32
1227 #define MAX_STITCH_LUXELS 64
1229 void StitchSurfaceLightmaps( void ){
1230 int i, j, x, y, x2, y2, *cluster, *cluster2,
1231 numStitched, numCandidates, numLuxels, f, fOld, start;
1232 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1233 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1234 sampleSize, average[ 3 ], totalColor, ootc;
1237 /* disabled for now */
1241 Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1245 start = I_FloatTime();
1247 /* walk the list of raw lightmaps */
1249 for ( i = 0; i < numRawLightmaps; i++ )
1251 /* print pacifier */
1252 f = 10 * i / numRawLightmaps;
1255 Sys_Printf( "%i...", f );
1258 /* get lightmap a */
1259 a = &rawLightmaps[ i ];
1261 /* walk rest of lightmaps */
1263 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1265 /* get lightmap b */
1266 b = &rawLightmaps[ j ];
1268 /* test bounding box */
1269 if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1270 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1271 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1276 c[ numCandidates++ ] = b;
1280 for ( y = 0; y < a->sh; y++ )
1282 for ( x = 0; x < a->sw; x++ )
1284 /* ignore unmapped/unlit luxels */
1286 cluster = SUPER_CLUSTER( x, y );
1287 if ( *cluster == CLUSTER_UNMAPPED ) {
1290 luxel = SUPER_LUXEL( 0, x, y );
1291 if ( luxel[ 3 ] <= 0.0f ) {
1295 /* get particulars */
1296 origin = SUPER_ORIGIN( x, y );
1297 normal = SUPER_NORMAL( x, y );
1299 /* walk candidate list */
1300 for ( j = 0; j < numCandidates; j++ )
1306 /* set samplesize to the smaller of the pair */
1307 sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1309 /* test bounding box */
1310 if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1311 origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1312 origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1316 /* walk candidate luxels */
1317 VectorClear( average );
1320 for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1322 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1324 /* ignore same luxels */
1325 if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1329 /* ignore unmapped/unlit luxels */
1330 cluster2 = SUPER_CLUSTER( x2, y2 );
1331 if ( *cluster2 == CLUSTER_UNMAPPED ) {
1334 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1335 if ( luxel2[ 3 ] <= 0.0f ) {
1339 /* get particulars */
1340 origin2 = SUPER_ORIGIN( x2, y2 );
1341 normal2 = SUPER_NORMAL( x2, y2 );
1344 if ( DotProduct( normal, normal2 ) < 0.5f ) {
1349 if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1350 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1351 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1356 //% VectorSet( luxel2, 255, 0, 255 );
1357 VectorAdd( average, luxel2, average );
1358 totalColor += luxel2[ 3 ];
1363 if ( numLuxels == 0 ) {
1368 ootc = 1.0f / totalColor;
1369 VectorScale( average, ootc, luxel );
1377 /* emit statistics */
1378 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1379 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1386 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1389 #define SOLID_EPSILON 0.0625
1390 #define LUXEL_TOLERANCE 0.0025
1391 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1393 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1396 double delta, total, rd, gd, bd;
1397 float *aLuxel, *bLuxel;
1400 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1401 if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1402 ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1407 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1408 a->brightness != b->brightness ||
1409 a->solid[ aNum ] != b->solid[ bNum ] ||
1410 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1414 /* compare solid color lightmaps */
1415 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1417 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1418 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1419 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1422 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1430 /* compare nonsolid lightmaps */
1431 if ( a->w != b->w || a->h != b->h ) {
1435 /* compare luxels */
1438 for ( y = 0; y < a->h; y++ )
1440 for ( x = 0; x < a->w; x++ )
1442 /* increment total */
1446 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1447 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1449 /* ignore unused luxels */
1450 if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1455 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1456 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1457 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1459 /* 2003-09-27: compare individual luxels */
1460 if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1464 /* compare (fixme: take into account perceptual differences) */
1465 delta += rd * LUXEL_COLOR_FRAC;
1466 delta += gd * LUXEL_COLOR_FRAC;
1467 delta += bd * LUXEL_COLOR_FRAC;
1469 /* is the change too high? */
1470 if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1476 /* made it this far, they must be identical (or close enough) */
1484 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1487 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1490 float luxel[ 3 ], *aLuxel, *bLuxel;
1494 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1495 a->brightness != b->brightness ||
1496 a->solid[ aNum ] != b->solid[ bNum ] ||
1497 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1501 /* compare solid lightmaps */
1502 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1504 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1505 VectorScale( luxel, 0.5f, luxel );
1508 VectorCopy( luxel, a->solidColor[ aNum ] );
1509 VectorCopy( luxel, b->solidColor[ bNum ] );
1511 /* return to sender */
1515 /* compare nonsolid lightmaps */
1516 if ( a->w != b->w || a->h != b->h ) {
1521 for ( y = 0; y < a->h; y++ )
1523 for ( x = 0; x < a->w; x++ )
1526 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1527 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1529 /* handle occlusion mismatch */
1530 if ( aLuxel[ 0 ] < 0.0f ) {
1531 VectorCopy( bLuxel, aLuxel );
1533 else if ( bLuxel[ 0 ] < 0.0f ) {
1534 VectorCopy( aLuxel, bLuxel );
1539 VectorAdd( aLuxel, bLuxel, luxel );
1540 VectorScale( luxel, 0.5f, luxel );
1542 /* debugging code */
1543 //% luxel[ 2 ] += 64.0f;
1546 VectorCopy( luxel, aLuxel );
1547 VectorCopy( luxel, bLuxel );
1560 determines if a single luxel is can be approximated with the interpolated vertex rgba
1563 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1564 int i, x, y, d, lightmapNum;
1566 vec3_t color, vertexColor;
1567 byte cb[ 4 ], vcb[ 4 ];
1570 /* find luxel xy coords */
1571 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1572 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1576 else if ( x >= lm->w ) {
1582 else if ( y >= lm->h ) {
1587 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1590 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1595 luxel = BSP_LUXEL( lightmapNum, x, y );
1597 /* ignore occluded luxels */
1598 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1602 /* copy, set min color and compare */
1603 VectorCopy( luxel, color );
1604 VectorCopy( dv->color[ 0 ], vertexColor );
1606 /* styles are not affected by minlight */
1607 if ( lightmapNum == 0 ) {
1608 for ( i = 0; i < 3; i++ )
1611 if ( color[ i ] < minLight[ i ] ) {
1612 color[ i ] = minLight[ i ];
1614 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1615 vertexColor[ i ] = minLight[ i ];
1621 ColorToBytes( color, cb, 1.0f );
1622 ColorToBytes( vertexColor, vcb, 1.0f );
1625 for ( i = 0; i < 3; i++ )
1627 d = cb[ i ] - vcb[ i ];
1631 if ( d > approximateTolerance ) {
1637 /* close enough for the girls i date */
1644 ApproximateTriangle()
1645 determines if a single triangle can be approximated with vertex rgba
1648 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1649 bspDrawVert_t mid, *dv2[ 3 ];
1653 /* approximate the vertexes */
1654 if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1657 if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1660 if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1664 /* subdivide calc */
1667 float dx, dy, dist, maxDist;
1670 /* find the longest edge and split it */
1673 for ( i = 0; i < 3; i++ )
1675 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1676 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1677 dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1678 if ( dist > maxDist ) {
1684 /* try to early out */
1685 if ( i < 0 || maxDist < subdivideThreshold ) {
1690 /* split the longest edge and map it */
1691 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1692 if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1696 /* recurse to first triangle */
1697 VectorCopy( dv, dv2 );
1699 if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1703 /* recurse to second triangle */
1704 VectorCopy( dv, dv2 );
1705 dv2[ ( max + 1 ) % 3 ] = ∣
1706 return ApproximateTriangle_r( lm, dv2 );
1712 ApproximateLightmap()
1713 determines if a raw lightmap can be approximated sufficiently with vertex colors
1716 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1717 int n, num, i, x, y, pw[ 5 ], r;
1718 bspDrawSurface_t *ds;
1719 surfaceInfo_t *info;
1720 mesh_t src, *subdivided, *mesh;
1721 bspDrawVert_t *verts, *dv[ 3 ];
1722 qboolean approximated;
1725 /* approximating? */
1726 if ( approximateTolerance <= 0 ) {
1730 /* test for jmonroe */
1732 /* don't approx lightmaps with styled twins */
1733 if ( lm->numStyledTwins > 0 ) {
1737 /* don't approx lightmaps with styles */
1738 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1740 if ( lm->styles[ i ] != LS_NONE ) {
1746 /* assume reduced until shadow detail is found */
1747 approximated = qtrue;
1749 /* walk the list of surfaces on this raw lightmap */
1750 for ( n = 0; n < lm->numLightSurfaces; n++ )
1753 num = lightSurfaces[ lm->firstLightSurface + n ];
1754 ds = &bspDrawSurfaces[ num ];
1755 info = &surfaceInfos[ num ];
1757 /* assume not-reduced initially */
1758 info->approximated = qfalse;
1760 /* bail if lightmap doesn't match up */
1761 if ( info->lm != lm ) {
1765 /* bail if not vertex lit */
1766 if ( info->si->noVertexLight ) {
1770 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1771 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1772 ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1773 ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1774 info->approximated = qtrue;
1775 numSurfsVertexForced++;
1779 /* handle the triangles */
1780 switch ( ds->surfaceType )
1784 verts = yDrawVerts + ds->firstVert;
1786 /* map the triangles */
1787 info->approximated = qtrue;
1788 for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1790 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1791 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1792 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1793 info->approximated = ApproximateTriangle_r( lm, dv );
1798 /* make a mesh from the drawsurf */
1799 src.width = ds->patchWidth;
1800 src.height = ds->patchHeight;
1801 src.verts = &yDrawVerts[ ds->firstVert ];
1802 //% subdivided = SubdivideMesh( src, 8, 512 );
1803 subdivided = SubdivideMesh2( src, info->patchIterations );
1805 /* fit it to the curve and remove colinear verts on rows/columns */
1806 PutMeshOnCurve( *subdivided );
1807 mesh = RemoveLinearMeshColumnsRows( subdivided );
1808 FreeMesh( subdivided );
1811 verts = mesh->verts;
1813 /* map the mesh quads */
1814 info->approximated = qtrue;
1815 for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1817 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1820 pw[ 0 ] = x + ( y * mesh->width );
1821 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1822 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1823 pw[ 3 ] = x + 1 + ( y * mesh->width );
1824 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1829 /* get drawverts and map first triangle */
1830 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1831 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1832 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1833 info->approximated = ApproximateTriangle_r( lm, dv );
1835 /* get drawverts and map second triangle */
1836 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1837 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1838 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1839 if ( info->approximated ) {
1840 info->approximated = ApproximateTriangle_r( lm, dv );
1854 if ( info->approximated == qfalse ) {
1855 approximated = qfalse;
1858 numSurfsVertexApproximated++;
1863 return approximated;
1869 TestOutLightmapStamp()
1870 tests a stamp on a given lightmap for validity
1873 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1874 int sx, sy, ox, oy, offset;
1879 if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1883 /* solid lightmaps test a 1x1 stamp */
1884 if ( lm->solid[ lightmapNum ] ) {
1885 offset = ( y * olm->customWidth ) + x;
1886 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1892 /* test the stamp */
1893 for ( sy = 0; sy < lm->h; sy++ )
1895 for ( sx = 0; sx < lm->w; sx++ )
1898 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1899 if ( luxel[ 0 ] < 0.0f ) {
1903 /* get bsp lightmap coords and test */
1906 offset = ( oy * olm->customWidth ) + ox;
1907 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1913 /* stamp is empty */
1921 sets up an output lightmap
1924 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1926 if ( lm == NULL || olm == NULL ) {
1930 /* is this a "normal" bsp-stored lightmap? */
1931 if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1932 olm->lightmapNum = numBSPLightmaps;
1935 /* lightmaps are interleaved with light direction maps */
1941 olm->lightmapNum = -3;
1944 /* set external lightmap number */
1945 olm->extLightmapNum = -1;
1948 olm->numLightmaps = 0;
1949 olm->customWidth = lm->customWidth;
1950 olm->customHeight = lm->customHeight;
1951 olm->freeLuxels = olm->customWidth * olm->customHeight;
1952 olm->numShaders = 0;
1954 /* allocate buffers */
1955 olm->lightBits = safe_malloc0( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1956 olm->bspLightBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
1958 olm->bspDirBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
1966 for a given surface lightmap, find output lightmap pages and positions for it
1969 #define LIGHTMAP_RESERVE_COUNT 1
1970 static void FindOutLightmaps( rawLightmap_t *lm, qboolean fastAllocate ){
1971 int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
1973 surfaceInfo_t *info;
1974 float *luxel, *deluxel;
1975 vec3_t color, direction;
1978 int xIncrement, yIncrement;
1980 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1981 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1982 lm->outLightmapNums[ lightmapNum ] = -3;
1984 /* can this lightmap be approximated with vertex color? */
1985 if ( ApproximateLightmap( lm ) ) {
1990 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1993 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1997 /* don't store twinned lightmaps */
1998 if ( lm->twins[ lightmapNum ] != NULL ) {
2002 /* if this is a styled lightmap, try some normalized locations first */
2004 if ( lightmapNum > 0 && outLightmaps != NULL ) {
2006 for ( j = 0; j < 2; j++ )
2008 /* try identical position */
2009 for ( i = 0; i < numOutLightmaps; i++ )
2011 /* get the output lightmap */
2012 olm = &outLightmaps[ i ];
2014 /* simple early out test */
2015 if ( olm->freeLuxels < lm->used ) {
2019 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2020 if ( olm->customWidth != lm->customWidth ||
2021 olm->customHeight != lm->customHeight ) {
2027 x = lm->lightmapX[ 0 ];
2028 y = lm->lightmapY[ 0 ];
2029 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2035 for ( sy = -1; sy <= 1; sy++ )
2037 for ( sx = -1; sx <= 1; sx++ )
2039 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
2040 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
2041 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2065 /* try normal placement algorithm */
2066 if ( ok == qfalse ) {
2071 /* walk the list of lightmap pages */
2072 if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
2076 i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
2078 for ( ; i < numOutLightmaps; i++ )
2080 /* get the output lightmap */
2081 olm = &outLightmaps[ i ];
2083 /* simple early out test */
2084 if ( olm->freeLuxels < lm->used ) {
2088 /* if fast allocation, skip lightmap files that are more than 90% complete */
2089 if ( fastAllocate == qtrue ) {
2090 if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
2095 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2096 if ( olm->customWidth != lm->customWidth ||
2097 olm->customHeight != lm->customHeight ) {
2102 if ( lm->solid[ lightmapNum ] ) {
2103 xMax = olm->customWidth;
2104 yMax = olm->customHeight;
2108 xMax = ( olm->customWidth - lm->w ) + 1;
2109 yMax = ( olm->customHeight - lm->h ) + 1;
2112 /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
2113 if ( fastAllocate == qtrue ) {
2114 xIncrement = MAX(1, lm->w / 15);
2115 yIncrement = MAX(1, lm->h / 15);
2122 /* walk the origin around the lightmap */
2123 for ( y = 0; y < yMax; y += yIncrement )
2125 for ( x = 0; x < xMax; x += xIncrement )
2127 /* find a fine tract of lauhnd */
2128 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2151 if ( ok == qfalse ) {
2152 /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2153 numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2154 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2155 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2156 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2157 free( outLightmaps );
2161 /* initialize both out lightmaps */
2162 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2163 SetupOutLightmap( lm, &outLightmaps[ k ] );
2165 /* set out lightmap */
2166 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2167 olm = &outLightmaps[ i ];
2169 /* set stamp xy origin to the first surface lightmap */
2170 if ( lightmapNum > 0 ) {
2171 x = lm->lightmapX[ 0 ];
2172 y = lm->lightmapY[ 0 ];
2176 /* if this is a style-using lightmap, it must be exported */
2177 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2178 olm->extLightmapNum = 0;
2181 /* add the surface lightmap to the bsp lightmap */
2182 lm->outLightmapNums[ lightmapNum ] = i;
2183 lm->lightmapX[ lightmapNum ] = x;
2184 lm->lightmapY[ lightmapNum ] = y;
2185 olm->numLightmaps++;
2188 for ( i = 0; i < lm->numLightSurfaces; i++ )
2190 /* get surface info */
2191 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2193 /* test for shader */
2194 for ( j = 0; j < olm->numShaders; j++ )
2196 if ( olm->shaders[ j ] == info->si ) {
2201 /* if it doesn't exist, add it */
2202 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2203 olm->shaders[ olm->numShaders ] = info->si;
2205 numLightmapShaders++;
2210 if ( lm->solid[ lightmapNum ] ) {
2220 /* mark the bits used */
2221 for ( y = 0; y < yMax; y++ )
2223 for ( x = 0; x < xMax; x++ )
2226 luxel = BSP_LUXEL( lightmapNum, x, y );
2227 deluxel = BSP_DELUXEL( x, y );
2228 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2232 /* set minimum light */
2233 if ( lm->solid[ lightmapNum ] ) {
2235 VectorSet( color, 255.0f, 0.0f, 0.0f );
2238 VectorCopy( lm->solidColor[ lightmapNum ], color );
2242 VectorCopy( luxel, color );
2245 /* styles are not affected by minlight */
2246 if ( lightmapNum == 0 ) {
2247 for ( i = 0; i < 3; i++ )
2249 if ( color[ i ] < minLight[ i ] ) {
2250 color[ i ] = minLight[ i ];
2255 /* get bsp lightmap coords */
2256 ox = x + lm->lightmapX[ lightmapNum ];
2257 oy = y + lm->lightmapY[ lightmapNum ];
2258 offset = ( oy * olm->customWidth ) + ox;
2260 /* flag pixel as used */
2261 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2265 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2266 ColorToBytes( color, pixel, lm->brightness );
2268 /* store direction */
2270 /* normalize average light direction */
2271 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2272 VectorScale( deluxel, 1000.0f, direction );
2273 VectorNormalize( direction, direction );
2274 VectorScale( direction, 127.5f, direction );
2275 for ( i = 0; i < 3; i++ )
2276 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2286 CompareRawLightmap()
2287 compare function for qsort()
2290 static int CompareRawLightmap( const void *a, const void *b ){
2291 rawLightmap_t *alm, *blm;
2292 surfaceInfo_t *aInfo, *bInfo;
2297 alm = &rawLightmaps[ *( (const int*) a ) ];
2298 blm = &rawLightmaps[ *( (const int*) b ) ];
2300 /* get min number of surfaces */
2301 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2304 for ( i = 0; i < min; i++ )
2306 /* get surface info */
2307 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2308 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2310 /* compare shader names */
2311 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2317 /* test style count */
2319 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2320 diff += blm->styles[ i ] - alm->styles[ i ];
2326 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2331 /* must be equivalent */
2337 void FillOutLightmap( outLightmap_t *olm ){
2340 vec3_t dir_sum, light_sum;
2342 byte *lightBitsNew = NULL;
2343 byte *lightBytesNew = NULL;
2344 byte *dirBytesNew = NULL;
2346 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2347 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2349 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2353 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2354 olm->lightBits[0] |= 1;
2355 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2356 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2357 olm->bspLightBytes[0] = 255;
2358 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2361 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2362 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2364 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2370 for ( y = 0; y < olm->customHeight; ++y )
2372 for ( x = 0; x < olm->customWidth; ++x )
2374 ofs = y * olm->customWidth + x;
2375 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2379 VectorClear( dir_sum );
2380 VectorClear( light_sum );
2382 /* try all four neighbors */
2383 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2384 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2386 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2388 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2392 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2393 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2395 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2397 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2401 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2402 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2404 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2406 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2410 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2411 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2413 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2415 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2421 ofs = y * olm->customWidth + x;
2422 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2423 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2425 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2435 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2436 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2438 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2442 free( lightBitsNew );
2443 free( lightBytesNew );
2445 free( dirBytesNew );
2452 StoreSurfaceLightmaps()
2453 stores the surface lightmaps into the bsp as byte rgb triplets
2456 void StoreSurfaceLightmaps( qboolean fastAllocate, qboolean storeForReal ){
2457 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2458 int style, size, lightmapNum, lightmapNum2;
2459 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2460 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2461 float *deluxel, *bspDeluxel, *bspDeluxel2;
2463 int numUsed, numTwins, numTwinLuxels, numStored;
2464 float lmx, lmy, efficiency;
2466 bspDrawSurface_t *ds, *parent, dsTemp;
2467 surfaceInfo_t *info;
2468 rawLightmap_t *lm, *lm2;
2470 bspDrawVert_t *dv, *ydv, *dvParent;
2471 char dirname[ 1024 ], filename[ 1024 ];
2473 char lightmapName[ 128 ];
2474 const char *rgbGenValues[ 256 ];
2475 const char *alphaGenValues[ 256 ];
2479 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2482 if ( lmCustomDir ) {
2483 strcpy( dirname, lmCustomDir );
2487 strcpy( dirname, source );
2488 StripExtension( dirname );
2490 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2491 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2493 /* -----------------------------------------------------------------
2494 average the sampled luxels into the bsp luxels
2495 ----------------------------------------------------------------- */
2498 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2500 /* walk the list of raw lightmaps */
2504 numSolidLightmaps = 0;
2505 for ( i = 0; i < numRawLightmaps; i++ )
2508 lm = &rawLightmaps[ i ];
2510 /* walk individual lightmaps */
2511 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2514 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2518 /* allocate bsp luxel storage */
2519 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2520 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2521 lm->bspLuxels[ lightmapNum ] = safe_malloc0( size );
2524 /* allocate radiosity lightmap storage */
2526 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2527 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2528 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2530 memset( lm->radLuxels[ lightmapNum ], 0, size );
2533 /* average supersampled luxels */
2534 for ( y = 0; y < lm->h; y++ )
2536 for ( x = 0; x < lm->w; x++ )
2540 occludedSamples = 0.0f;
2542 VectorClear( sample );
2543 VectorClear( occludedSample );
2544 VectorClear( dirSample );
2545 for ( ly = 0; ly < superSample; ly++ )
2547 for ( lx = 0; lx < superSample; lx++ )
2550 sx = x * superSample + lx;
2551 sy = y * superSample + ly;
2552 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2553 deluxel = SUPER_DELUXEL( sx, sy );
2554 normal = SUPER_NORMAL( sx, sy );
2555 cluster = SUPER_CLUSTER( sx, sy );
2557 /* sample deluxemap */
2558 if ( deluxemap && lightmapNum == 0 ) {
2559 VectorAdd( dirSample, deluxel, dirSample );
2562 /* keep track of used/occluded samples */
2563 if ( *cluster != CLUSTER_UNMAPPED ) {
2567 /* handle lightmap border? */
2568 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2569 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2574 else if ( debug && *cluster < 0 ) {
2575 if ( *cluster == CLUSTER_UNMAPPED ) {
2576 VectorSet( luxel, 255, 204, 0 );
2578 else if ( *cluster == CLUSTER_OCCLUDED ) {
2579 VectorSet( luxel, 255, 0, 255 );
2581 else if ( *cluster == CLUSTER_FLOODED ) {
2582 VectorSet( luxel, 0, 32, 255 );
2584 VectorAdd( occludedSample, luxel, occludedSample );
2585 occludedSamples += 1.0f;
2588 /* normal luxel handling */
2589 else if ( luxel[ 3 ] > 0.0f ) {
2590 /* handle lit or flooded luxels */
2591 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2592 VectorAdd( sample, luxel, sample );
2593 samples += luxel[ 3 ];
2596 /* handle occluded or unmapped luxels */
2599 VectorAdd( occludedSample, luxel, occludedSample );
2600 occludedSamples += luxel[ 3 ];
2603 /* handle style debugging */
2604 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2605 VectorCopy( debugColors[ 0 ], sample );
2612 /* only use occluded samples if necessary */
2613 if ( samples <= 0.0f ) {
2614 VectorCopy( occludedSample, sample );
2615 samples = occludedSamples;
2619 luxel = SUPER_LUXEL( lightmapNum, x, y );
2620 deluxel = SUPER_DELUXEL( x, y );
2622 /* store light direction */
2623 if ( deluxemap && lightmapNum == 0 ) {
2624 VectorCopy( dirSample, deluxel );
2627 /* store the sample back in super luxels */
2628 if ( samples > 0.01f ) {
2629 VectorScale( sample, ( 1.0f / samples ), luxel );
2633 /* if any samples were mapped in any way, store ambient color */
2634 else if ( mappedSamples > 0 ) {
2635 if ( lightmapNum == 0 ) {
2636 VectorCopy( ambientColor, luxel );
2639 VectorClear( luxel );
2644 /* store a bogus value to be fixed later */
2647 VectorClear( luxel );
2655 ClearBounds( colorMins, colorMaxs );
2657 /* clean up and store into bsp luxels */
2658 for ( y = 0; y < lm->h; y++ )
2660 for ( x = 0; x < lm->w; x++ )
2663 luxel = SUPER_LUXEL( lightmapNum, x, y );
2664 deluxel = SUPER_DELUXEL( x, y );
2666 /* copy light direction */
2667 if ( deluxemap && lightmapNum == 0 ) {
2668 VectorCopy( deluxel, dirSample );
2671 /* is this a valid sample? */
2672 if ( luxel[ 3 ] > 0.0f ) {
2673 VectorCopy( luxel, sample );
2674 samples = luxel[ 3 ];
2678 /* fix negative samples */
2679 for ( j = 0; j < 3; j++ )
2681 if ( sample[ j ] < 0.0f ) {
2688 /* nick an average value from the neighbors */
2689 VectorClear( sample );
2690 VectorClear( dirSample );
2693 /* fixme: why is this disabled?? */
2694 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2696 if ( sy < 0 || sy >= lm->h ) {
2700 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2702 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2706 /* get neighbor's particulars */
2707 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2708 if ( luxel[ 3 ] < 0.0f ) {
2711 VectorAdd( sample, luxel, sample );
2712 samples += luxel[ 3 ];
2717 if ( samples == 0.0f ) {
2718 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2726 /* fix negative samples */
2727 for ( j = 0; j < 3; j++ )
2729 if ( sample[ j ] < 0.0f ) {
2736 /* scale the sample */
2737 VectorScale( sample, ( 1.0f / samples ), sample );
2739 /* store the sample in the radiosity luxels */
2741 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2742 VectorCopy( sample, radLuxel );
2744 /* if only storing bounced light, early out here */
2745 if ( bounceOnly && !bouncing ) {
2750 /* store the sample in the bsp luxels */
2751 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2752 bspDeluxel = BSP_DELUXEL( x, y );
2754 VectorAdd( bspLuxel, sample, bspLuxel );
2755 if ( deluxemap && lightmapNum == 0 ) {
2756 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2759 /* add color to bounds for solid checking */
2760 if ( samples > 0.0f ) {
2761 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2766 /* set solid color */
2767 lm->solid[ lightmapNum ] = qfalse;
2768 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2769 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2771 /* nocollapse prevents solid lightmaps */
2772 if ( noCollapse == qfalse ) {
2773 /* check solid color */
2774 VectorSubtract( colorMaxs, colorMins, sample );
2775 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2776 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2778 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2779 lm->solid[ lightmapNum ] = qtrue;
2780 numSolidLightmaps++;
2783 /* if all lightmaps aren't solid, then none of them are solid */
2784 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2785 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2787 if ( lm->solid[ y ] ) {
2788 numSolidLightmaps--;
2790 lm->solid[ y ] = qfalse;
2795 /* wrap bsp luxels if necessary */
2796 if ( lm->wrap[ 0 ] ) {
2797 for ( y = 0; y < lm->h; y++ )
2799 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2800 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2801 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2802 VectorScale( bspLuxel, 0.5f, bspLuxel );
2803 VectorCopy( bspLuxel, bspLuxel2 );
2804 if ( deluxemap && lightmapNum == 0 ) {
2805 bspDeluxel = BSP_DELUXEL( 0, y );
2806 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2807 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2808 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2809 VectorCopy( bspDeluxel, bspDeluxel2 );
2813 if ( lm->wrap[ 1 ] ) {
2814 for ( x = 0; x < lm->w; x++ )
2816 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2817 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2818 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2819 VectorScale( bspLuxel, 0.5f, bspLuxel );
2820 VectorCopy( bspLuxel, bspLuxel2 );
2821 if ( deluxemap && lightmapNum == 0 ) {
2822 bspDeluxel = BSP_DELUXEL( x, 0 );
2823 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2824 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2825 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2826 VectorCopy( bspDeluxel, bspDeluxel2 );
2833 /* -----------------------------------------------------------------
2834 convert modelspace deluxemaps to tangentspace
2835 ----------------------------------------------------------------- */
2838 if ( deluxemap && deluxemode == 1 ) {
2839 vec3_t worldUp, myNormal, myTangent, myBinormal;
2842 Sys_Printf( "converting..." );
2844 for ( i = 0; i < numRawLightmaps; i++ )
2847 lm = &rawLightmaps[ i ];
2849 /* walk lightmap samples */
2850 for ( y = 0; y < lm->sh; y++ )
2852 for ( x = 0; x < lm->sw; x++ )
2854 /* get normal and deluxel */
2855 normal = SUPER_NORMAL( x, y );
2856 cluster = SUPER_CLUSTER( x, y );
2857 bspDeluxel = BSP_DELUXEL( x, y );
2858 deluxel = SUPER_DELUXEL( x, y );
2861 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2863 /* get tangent vectors */
2864 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2865 if ( myNormal[ 2 ] == 1.0f ) {
2866 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2867 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2869 else if ( myNormal[ 2 ] == -1.0f ) {
2870 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2871 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2876 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2877 CrossProduct( myNormal, worldUp, myTangent );
2878 VectorNormalize( myTangent, myTangent );
2879 CrossProduct( myTangent, myNormal, myBinormal );
2880 VectorNormalize( myBinormal, myBinormal );
2883 /* project onto plane */
2884 dist = -DotProduct( myTangent, myNormal );
2885 VectorMA( myTangent, dist, myNormal, myTangent );
2886 dist = -DotProduct( myBinormal, myNormal );
2887 VectorMA( myBinormal, dist, myNormal, myBinormal );
2890 VectorNormalize( myTangent, myTangent );
2891 VectorNormalize( myBinormal, myBinormal );
2893 /* convert modelspace deluxel to tangentspace */
2894 dirSample[0] = bspDeluxel[0];
2895 dirSample[1] = bspDeluxel[1];
2896 dirSample[2] = bspDeluxel[2];
2897 VectorNormalize( dirSample, dirSample );
2899 /* fix tangents to world matrix */
2900 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2901 VectorNegate( myTangent, myTangent );
2904 /* build tangentspace vectors */
2905 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2906 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2907 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2914 /* -----------------------------------------------------------------
2916 ----------------------------------------------------------------- */
2918 #ifdef sdfsdfwq312323
2920 Sys_Printf( "blending..." );
2922 for ( i = 0; i < numRawLightmaps; i++ )
2928 lm = &rawLightmaps[ i ];
2930 /* walk individual lightmaps */
2931 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2934 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2938 /* walk lightmap samples */
2939 for ( y = 0; y < lm->sh; y++ )
2941 for ( x = 0; x < lm->sw; x++ )
2944 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2947 VectorNormalize( bspLuxel, myColor );
2948 myBrightness = VectorLength( bspLuxel );
2949 myBrightness *= ( 1 / 127.0f );
2950 myBrightness = myBrightness * myBrightness;
2951 myBrightness *= 127.0f;
2952 VectorScale( myColor, myBrightness, bspLuxel );
2959 /* -----------------------------------------------------------------
2960 collapse non-unique lightmaps
2961 ----------------------------------------------------------------- */
2963 if ( storeForReal && noCollapse == qfalse && deluxemap == qfalse ) {
2965 Sys_FPrintf( SYS_VRB, "collapsing..." );
2967 /* set all twin refs to null */
2968 for ( i = 0; i < numRawLightmaps; i++ )
2970 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2972 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2973 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2974 rawLightmaps[ i ].numStyledTwins = 0;
2978 /* walk the list of raw lightmaps */
2979 for ( i = 0; i < numRawLightmaps; i++ )
2982 lm = &rawLightmaps[ i ];
2984 /* walk lightmaps */
2985 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2988 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
2989 lm->twins[ lightmapNum ] != NULL ) {
2993 /* find all lightmaps that are virtually identical to this one */
2994 for ( j = i + 1; j < numRawLightmaps; j++ )
2997 lm2 = &rawLightmaps[ j ];
2999 /* walk lightmaps */
3000 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3003 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3004 lm2->twins[ lightmapNum2 ] != NULL ) {
3009 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3010 /* merge and set twin */
3011 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3012 lm2->twins[ lightmapNum2 ] = lm;
3013 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3015 numTwinLuxels += ( lm->w * lm->h );
3017 /* count styled twins */
3018 if ( lightmapNum > 0 ) {
3019 lm->numStyledTwins++;
3029 /* -----------------------------------------------------------------
3030 sort raw lightmaps by shader
3031 ----------------------------------------------------------------- */
3034 Sys_FPrintf( SYS_VRB, "sorting..." );
3036 /* allocate a new sorted list */
3037 if ( sortLightmaps == NULL ) {
3038 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3041 /* fill it out and sort it */
3042 for ( i = 0; i < numRawLightmaps; i++ )
3043 sortLightmaps[ i ] = i;
3044 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3046 /* -----------------------------------------------------------------
3047 allocate output lightmaps
3048 ----------------------------------------------------------------- */
3050 if ( storeForReal ) {
3052 Sys_FPrintf( SYS_VRB, "allocating..." );
3054 /* kill all existing output lightmaps */
3055 if ( outLightmaps != NULL ) {
3056 for ( i = 0; i < numOutLightmaps; i++ )
3058 free( outLightmaps[ i ].lightBits );
3059 free( outLightmaps[ i ].bspLightBytes );
3061 free( outLightmaps );
3062 outLightmaps = NULL;
3065 numLightmapShaders = 0;
3066 numOutLightmaps = 0;
3067 numBSPLightmaps = 0;
3068 numExtLightmaps = 0;
3070 /* find output lightmap */
3071 for ( i = 0; i < numRawLightmaps; i++ )
3073 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3074 FindOutLightmaps( lm, fastAllocate );
3077 /* set output numbers in twinned lightmaps */
3078 for ( i = 0; i < numRawLightmaps; i++ )
3081 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3083 /* walk lightmaps */
3084 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3087 lm2 = lm->twins[ lightmapNum ];
3088 if ( lm2 == NULL ) {
3091 lightmapNum2 = lm->twinNums[ lightmapNum ];
3093 /* find output lightmap from twin */
3094 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3095 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3096 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3101 /* -----------------------------------------------------------------
3102 store output lightmaps
3103 ----------------------------------------------------------------- */
3105 if ( storeForReal ) {
3107 Sys_FPrintf( SYS_VRB, "storing..." );
3109 /* count the bsp lightmaps and allocate space */
3110 if ( bspLightBytes != NULL ) {
3111 free( bspLightBytes );
3113 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3114 numBSPLightBytes = 0;
3115 bspLightBytes = NULL;
3119 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3120 bspLightBytes = safe_malloc0( numBSPLightBytes );
3123 /* walk the list of output lightmaps */
3124 for ( i = 0; i < numOutLightmaps; i++ )
3126 /* get output lightmap */
3127 olm = &outLightmaps[ i ];
3129 /* fill output lightmap */
3130 if ( lightmapFill ) {
3131 FillOutLightmap( olm );
3134 /* is this a valid bsp lightmap? */
3135 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3136 /* copy lighting data */
3137 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3138 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3140 /* copy direction data */
3142 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3143 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3147 /* external lightmap? */
3148 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3149 /* make a directory for the lightmaps */
3152 /* set external lightmap number */
3153 olm->extLightmapNum = numExtLightmaps;
3155 /* write lightmap */
3156 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3157 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3158 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3161 /* write deluxemap */
3163 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3164 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3165 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3168 if ( debugDeluxemap ) {
3169 olm->extLightmapNum++;
3175 if ( numExtLightmaps > 0 ) {
3176 Sys_FPrintf( SYS_VRB, "\n" );
3179 /* delete unused external lightmaps */
3180 for ( i = numExtLightmaps; i; i++ )
3182 /* determine if file exists */
3183 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3184 if ( !FileExists( filename ) ) {
3193 /* -----------------------------------------------------------------
3194 project the lightmaps onto the bsp surfaces
3195 ----------------------------------------------------------------- */
3197 if ( storeForReal ) {
3199 Sys_FPrintf( SYS_VRB, "projecting..." );
3201 /* walk the list of surfaces */
3202 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3204 /* get the surface and info */
3205 ds = &bspDrawSurfaces[ i ];
3206 info = &surfaceInfos[ i ];
3210 /* handle surfaces with identical parent */
3211 if ( info->parentSurfaceNum >= 0 ) {
3212 /* preserve original data and get parent */
3213 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3214 memcpy( &dsTemp, ds, sizeof( *ds ) );
3216 /* overwrite child with parent data */
3217 memcpy( ds, parent, sizeof( *ds ) );
3219 /* restore key parts */
3220 ds->fogNum = dsTemp.fogNum;
3221 ds->firstVert = dsTemp.firstVert;
3222 ds->firstIndex = dsTemp.firstIndex;
3223 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3225 /* set vertex data */
3226 dv = &bspDrawVerts[ ds->firstVert ];
3227 dvParent = &bspDrawVerts[ parent->firstVert ];
3228 for ( j = 0; j < ds->numVerts; j++ )
3230 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3231 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3238 /* handle vertex lit or approximated surfaces */
3239 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3240 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3242 ds->lightmapNum[ lightmapNum ] = -3;
3243 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3247 /* handle lightmapped surfaces */
3250 /* walk lightmaps */
3251 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3254 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3256 /* handle unused style */
3257 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3258 ds->lightmapNum[ lightmapNum ] = -3;
3262 /* get output lightmap */
3263 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3265 /* set bsp lightmap number */
3266 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3268 /* deluxemap debugging makes the deluxemap visible */
3269 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3270 ds->lightmapNum[ lightmapNum ]++;
3273 /* calc lightmap origin in texture space */
3274 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3275 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3277 /* calc lightmap st coords */
3278 dv = &bspDrawVerts[ ds->firstVert ];
3279 ydv = &yDrawVerts[ ds->firstVert ];
3280 for ( j = 0; j < ds->numVerts; j++ )
3282 if ( lm->solid[ lightmapNum ] ) {
3283 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3284 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3288 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3289 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3295 /* store vertex colors */
3296 dv = &bspDrawVerts[ ds->firstVert ];
3297 for ( j = 0; j < ds->numVerts; j++ )
3299 /* walk lightmaps */
3300 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3302 /* handle unused style */
3303 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3304 VectorClear( color );
3308 /* get vertex color */
3309 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3310 VectorCopy( luxel, color );
3312 /* set minimum light */
3313 if ( lightmapNum == 0 ) {
3314 for ( k = 0; k < 3; k++ )
3315 if ( color[ k ] < minVertexLight[ k ] ) {
3316 color[ k ] = minVertexLight[ k ];
3321 /* store to bytes */
3322 if ( !info->si->noVertexLight ) {
3323 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3328 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3329 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3331 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3335 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3336 dv = &bspDrawVerts[ ds->firstVert ];
3338 /* depthFunc equal? */
3339 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3346 /* generate stages for styled lightmaps */
3347 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3350 style = lm->styles[ lightmapNum ];
3351 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3355 /* get output lightmap */
3356 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3359 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3360 strcpy( lightmapName, "$lightmap" );
3363 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3366 /* get rgbgen string */
3367 if ( rgbGenValues[ style ] == NULL ) {
3368 sprintf( key, "_style%drgbgen", style );
3369 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3370 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3371 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3375 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3376 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3382 /* get alphagen string */
3383 if ( alphaGenValues[ style ] == NULL ) {
3384 sprintf( key, "_style%dalphagen", style );
3385 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3387 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3388 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3391 alphaGen[ 0 ] = '\0';
3394 /* calculate st offset */
3395 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3396 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3398 /* create additional stage */
3399 if ( lmx == 0.0f && lmy == 0.0f ) {
3400 sprintf( styleStage, "\t{\n"
3401 "\t\tmap %s\n" /* lightmap */
3402 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3403 "%s" /* depthFunc equal */
3406 "\t\ttcGen lightmap\n"
3409 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3415 sprintf( styleStage, "\t{\n"
3416 "\t\tmap %s\n" /* lightmap */
3417 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3418 "%s" /* depthFunc equal */
3421 "\t\ttcGen lightmap\n"
3422 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3425 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3433 strcat( styleStages, styleStage );
3436 /* create custom shader */
3437 if ( info->si->styleMarker == 2 ) {
3438 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3441 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3444 /* emit remap command */
3445 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3448 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3449 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3450 //% Sys_Printf( ")\n" );
3453 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3454 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3455 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3456 /* get output lightmap */
3457 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3459 /* do some name mangling */
3460 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3462 /* create custom shader */
3463 csi = CustomShader( info->si, "$lightmap", lightmapName );
3466 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3467 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3468 //% Sys_Printf( ")\n" );
3471 /* use the normal plain-jane shader */
3473 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3479 Sys_FPrintf( SYS_VRB, "done.\n" );
3481 /* calc num stored */
3482 numStored = numBSPLightBytes / 3;
3483 efficiency = ( numStored <= 0 )
3485 : (float) numUsed / (float) numStored;
3487 if ( storeForReal ) {
3489 Sys_Printf( "%9d luxels used\n", numUsed );
3490 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3491 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3492 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3493 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3494 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3495 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3496 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3497 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3499 /* write map shader file */
3500 WriteMapShaderFile();