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 if ( debugSampleSize == 1 || lm->customWidth > 128 ){
700 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 else if ( debugSampleSize == 0 ){
711 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",
727 /* set actual sample size */
728 lm->actualSampleSize = sampleSize;
730 /* fixme: copy rounded mins/maxes to lightmap record? */
731 if ( lm->plane == NULL ) {
732 VectorCopy( mins, lm->mins );
733 VectorCopy( maxs, lm->maxs );
734 VectorCopy( mins, origin );
737 /* set lightmap origin */
738 VectorCopy( lm->mins, origin );
740 /* make absolute axis */
741 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
742 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
743 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
745 /* clear out lightmap vectors */
746 memset( vecs, 0, sizeof( vecs ) );
748 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
749 if ( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] ) {
753 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
754 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
756 else if ( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] ) {
760 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
761 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
768 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
769 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
772 /* check for bogus axis */
773 if ( faxis[ axisNum ] == 0.0f ) {
774 Sys_FPrintf( SYS_WRN, "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
779 /* store the axis number in the lightmap */
780 lm->axisNum = axisNum;
782 /* walk the list of surfaces on this raw lightmap */
783 for ( n = 0; n < lm->numLightSurfaces; n++ )
786 num2 = lightSurfaces[ lm->firstLightSurface + n ];
787 ds2 = &bspDrawSurfaces[ num2 ];
788 verts = &yDrawVerts[ ds2->firstVert ];
790 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
791 for ( i = 0; i < ds2->numVerts; i++ )
793 VectorSubtract( verts[ i ].xyz, origin, delta );
794 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
795 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
796 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
797 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
799 if ( s > (float) lm->w || t > (float) lm->h ) {
800 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
801 s, lm->w, t, lm->h );
806 /* get first drawsurface */
807 num2 = lightSurfaces[ lm->firstLightSurface ];
808 ds2 = &bspDrawSurfaces[ num2 ];
809 verts = &yDrawVerts[ ds2->firstVert ];
811 /* calculate lightmap origin */
812 if ( VectorLength( ds2->lightmapVecs[ 2 ] ) ) {
813 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
816 VectorCopy( lm->axis, plane );
818 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
820 VectorCopy( origin, lm->origin );
821 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
822 d /= plane[ axisNum ];
823 lm->origin[ axisNum ] -= d;
826 VectorCopy( lm->origin, ds->lightmapOrigin );
828 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
829 if ( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) { /* ydnar: can't remember what exactly i was thinking here... */
830 /* allocate space for the vectors */
831 lm->vecs = safe_malloc0( 3 * sizeof( vec3_t ) );
832 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
834 /* project stepped lightmap blocks and subtract to get planevecs */
835 for ( i = 0; i < 2; i++ )
837 len = VectorNormalize( vecs[ i ], normalized );
838 VectorScale( normalized, ( 1.0 / len ), lm->vecs[ i ] );
839 d = DotProduct( lm->vecs[ i ], plane );
840 d /= plane[ axisNum ];
841 lm->vecs[ i ][ axisNum ] -= d;
846 /* lightmap vectors are useless on a non-planar surface */
851 if ( ds->surfaceType == MST_PATCH ) {
852 numPatchesLightmapped++;
853 if ( lm->plane != NULL ) {
854 numPlanarPatchesLightmapped++;
859 if ( lm->plane != NULL ) {
860 numPlanarsLightmapped++;
863 numNonPlanarsLightmapped++;
875 compare function for qsort()
878 static int CompareSurfaceInfo( const void *a, const void *b ){
879 surfaceInfo_t *aInfo, *bInfo;
883 /* get surface info */
884 aInfo = &surfaceInfos[ *( (const int*) a ) ];
885 bInfo = &surfaceInfos[ *( (const int*) b ) ];
888 if ( aInfo->modelindex < bInfo->modelindex ) {
891 else if ( aInfo->modelindex > bInfo->modelindex ) {
895 /* then lightmap status */
896 if ( aInfo->hasLightmap < bInfo->hasLightmap ) {
899 else if ( aInfo->hasLightmap > bInfo->hasLightmap ) {
903 /* 27: then shader! */
904 if ( aInfo->si < bInfo->si ) {
907 else if ( aInfo->si > bInfo->si ) {
911 /* then lightmap sample size */
912 if ( aInfo->sampleSize < bInfo->sampleSize ) {
915 else if ( aInfo->sampleSize > bInfo->sampleSize ) {
919 /* then lightmap axis */
920 for ( i = 0; i < 3; i++ )
922 if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
925 else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
931 if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
934 else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
937 else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
938 for ( i = 0; i < 4; i++ )
940 if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
943 else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
949 /* then position in world */
950 for ( i = 0; i < 3; i++ )
952 if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
955 else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
960 /* these are functionally identical (this should almost never happen) */
967 SetupSurfaceLightmaps()
968 allocates lightmaps for every surface in the bsp that needs one
969 this depends on yDrawVerts being allocated
972 void SetupSurfaceLightmaps( void ){
973 int i, j, k, s,num, num2;
976 bspDrawSurface_t *ds;
977 surfaceInfo_t *info, *info2;
980 vec3_t mapSize, entityOrigin;
984 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
986 /* determine supersample amount */
987 if ( superSample < 1 ) {
990 else if ( superSample > 8 ) {
991 Sys_FPrintf( SYS_WRN, "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
995 /* clear map bounds */
996 ClearBounds( mapMins, mapMaxs );
998 /* allocate a list of surface clusters */
999 numSurfaceClusters = 0;
1000 maxSurfaceClusters = numBSPLeafSurfaces;
1001 surfaceClusters = safe_malloc0( maxSurfaceClusters * sizeof( *surfaceClusters ) );
1003 /* allocate a list for per-surface info */
1004 surfaceInfos = safe_malloc0( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
1005 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1006 surfaceInfos[ i ].childSurfaceNum = -1;
1008 /* allocate a list of surface indexes to be sorted */
1009 sortSurfaces = safe_malloc0( numBSPDrawSurfaces * sizeof( int ) );
1011 /* walk each model in the bsp */
1012 for ( i = 0; i < numBSPModels; i++ )
1015 model = &bspModels[ i ];
1017 /* walk the list of surfaces in this model and fill out the info structs */
1018 for ( j = 0; j < model->numBSPSurfaces; j++ )
1020 /* make surface index */
1021 num = model->firstBSPSurface + j;
1023 /* copy index to sort list */
1024 sortSurfaces[ num ] = num;
1026 /* get surface and info */
1027 ds = &bspDrawSurfaces[ num ];
1028 info = &surfaceInfos[ num ];
1030 /* set entity origin */
1031 if ( ds->numVerts > 0 ) {
1032 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
1035 VectorClear( entityOrigin );
1039 info->modelindex = i;
1042 info->firstSurfaceCluster = numSurfaceClusters;
1044 /* get extra data */
1045 info->si = GetSurfaceExtraShaderInfo( num );
1046 if ( info->si == NULL ) {
1047 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
1049 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1050 info->entityNum = GetSurfaceExtraEntityNum( num );
1051 info->castShadows = GetSurfaceExtraCastShadows( num );
1052 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1053 info->sampleSize = GetSurfaceExtraSampleSize( num );
1054 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1055 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1056 GetSurfaceExtraLightmapAxis( num, info->axis );
1059 if ( info->parentSurfaceNum >= 0 ) {
1060 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1063 /* determine surface bounds */
1064 ClearBounds( info->mins, info->maxs );
1065 for ( k = 0; k < ds->numVerts; k++ )
1067 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1068 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1071 /* find all the bsp clusters the surface falls into */
1072 for ( k = 0; k < numBSPLeafs; k++ )
1075 leaf = &bspLeafs[ k ];
1078 if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1079 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1080 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
1084 /* test leaf surfaces */
1085 for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1087 if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
1088 if ( numSurfaceClusters >= maxSurfaceClusters ) {
1089 Error( "maxSurfaceClusters exceeded" );
1091 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1092 numSurfaceClusters++;
1093 info->numSurfaceClusters++;
1098 /* determine if surface is planar */
1099 if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
1101 info->plane = safe_malloc( 4 * sizeof( float ) );
1102 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1103 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1106 /* determine if surface requires a lightmap */
1107 if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
1108 ds->surfaceType == MST_FOLIAGE ||
1109 ( info->si->compileFlags & C_VERTEXLIT ) ||
1111 numSurfsVertexLit++;
1115 numSurfsLightmapped++;
1116 info->hasLightmap = qtrue;
1121 /* find longest map distance */
1122 VectorSubtract( mapMaxs, mapMins, mapSize );
1123 maxMapDistance = VectorLength( mapSize );
1125 /* sort the surfaces info list */
1126 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1128 /* allocate a list of surfaces that would go into raw lightmaps */
1129 numLightSurfaces = 0;
1130 lightSurfaces = safe_malloc0( numSurfsLightmapped * sizeof( int ) );
1132 /* allocate a list of raw lightmaps */
1133 numRawSuperLuxels = 0;
1134 numRawLightmaps = 0;
1135 rawLightmaps = safe_malloc0( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1137 /* walk the list of sorted surfaces */
1138 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1140 /* get info and attempt early out */
1141 num = sortSurfaces[ i ];
1142 ds = &bspDrawSurfaces[ num ];
1143 info = &surfaceInfos[ num ];
1144 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1148 /* allocate a new raw lightmap */
1149 lm = &rawLightmaps[ numRawLightmaps ];
1153 lm->splotchFix = info->si->splotchFix;
1154 lm->firstLightSurface = numLightSurfaces;
1155 lm->numLightSurfaces = 0;
1156 /* vortex: multiply lightmap sample size by -samplescale */
1157 if ( sampleScale > 0 ) {
1158 lm->sampleSize = info->sampleSize * sampleScale;
1161 lm->sampleSize = info->sampleSize;
1163 lm->actualSampleSize = lm->sampleSize;
1164 lm->entityNum = info->entityNum;
1165 lm->recvShadows = info->recvShadows;
1166 lm->brightness = info->si->lmBrightness;
1167 lm->filterRadius = info->si->lmFilterRadius;
1168 VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
1169 lm->floodlightDistance = info->si->floodlightDistance;
1170 lm->floodlightIntensity = info->si->floodlightIntensity;
1171 lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
1172 VectorCopy( info->axis, lm->axis );
1173 lm->plane = info->plane;
1174 VectorCopy( info->mins, lm->mins );
1175 VectorCopy( info->maxs, lm->maxs );
1177 lm->customWidth = info->si->lmCustomWidth;
1178 lm->customHeight = info->si->lmCustomHeight;
1180 /* add the surface to the raw lightmap */
1181 AddSurfaceToRawLightmap( num, lm );
1184 /* do an exhaustive merge */
1188 /* walk the list of surfaces again */
1190 for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1192 /* get info and attempt early out */
1193 num2 = sortSurfaces[ j ];
1194 info2 = &surfaceInfos[ num2 ];
1195 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1199 /* add the surface to the raw lightmap */
1200 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1207 lm->numLightSurfaces--;
1213 /* finish the lightmap and allocate the various buffers */
1214 FinishRawLightmap( lm );
1217 if ( debugSampleSize < -1 ){
1218 Sys_FPrintf( SYS_VRB, "+%d similar occurrences;\t-debugSampleSize to show ones\n", -debugSampleSize - 1 );
1221 /* allocate vertex luxel storage */
1222 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1224 vertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1225 radVertexLuxels[ k ] = safe_malloc0( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1228 /* emit some stats */
1229 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1230 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1231 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1232 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1233 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1234 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1235 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1236 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1242 StitchSurfaceLightmaps()
1243 stitches lightmap edges
1244 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1247 #define MAX_STITCH_CANDIDATES 32
1248 #define MAX_STITCH_LUXELS 64
1250 void StitchSurfaceLightmaps( void ){
1251 int i, j, x, y, x2, y2, *cluster, *cluster2,
1252 numStitched, numCandidates, numLuxels, f, fOld, start;
1253 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1254 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1255 sampleSize, average[ 3 ], totalColor, ootc;
1258 /* disabled for now */
1262 Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1266 start = I_FloatTime();
1268 /* walk the list of raw lightmaps */
1270 for ( i = 0; i < numRawLightmaps; i++ )
1272 /* print pacifier */
1273 f = 10 * i / numRawLightmaps;
1276 Sys_Printf( "%i...", f );
1279 /* get lightmap a */
1280 a = &rawLightmaps[ i ];
1282 /* walk rest of lightmaps */
1284 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1286 /* get lightmap b */
1287 b = &rawLightmaps[ j ];
1289 /* test bounding box */
1290 if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1291 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1292 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1297 c[ numCandidates++ ] = b;
1301 for ( y = 0; y < a->sh; y++ )
1303 for ( x = 0; x < a->sw; x++ )
1305 /* ignore unmapped/unlit luxels */
1307 cluster = SUPER_CLUSTER( x, y );
1308 if ( *cluster == CLUSTER_UNMAPPED ) {
1311 luxel = SUPER_LUXEL( 0, x, y );
1312 if ( luxel[ 3 ] <= 0.0f ) {
1316 /* get particulars */
1317 origin = SUPER_ORIGIN( x, y );
1318 normal = SUPER_NORMAL( x, y );
1320 /* walk candidate list */
1321 for ( j = 0; j < numCandidates; j++ )
1327 /* set samplesize to the smaller of the pair */
1328 sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1330 /* test bounding box */
1331 if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1332 origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1333 origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1337 /* walk candidate luxels */
1338 VectorClear( average );
1341 for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1343 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1345 /* ignore same luxels */
1346 if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1350 /* ignore unmapped/unlit luxels */
1351 cluster2 = SUPER_CLUSTER( x2, y2 );
1352 if ( *cluster2 == CLUSTER_UNMAPPED ) {
1355 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1356 if ( luxel2[ 3 ] <= 0.0f ) {
1360 /* get particulars */
1361 origin2 = SUPER_ORIGIN( x2, y2 );
1362 normal2 = SUPER_NORMAL( x2, y2 );
1365 if ( DotProduct( normal, normal2 ) < 0.5f ) {
1370 if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1371 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1372 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1377 //% VectorSet( luxel2, 255, 0, 255 );
1378 VectorAdd( average, luxel2, average );
1379 totalColor += luxel2[ 3 ];
1384 if ( numLuxels == 0 ) {
1389 ootc = 1.0f / totalColor;
1390 VectorScale( average, ootc, luxel );
1398 /* emit statistics */
1399 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1400 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1407 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1410 #define SOLID_EPSILON 0.0625
1411 #define LUXEL_TOLERANCE 0.0025
1412 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1414 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1417 double delta, total, rd, gd, bd;
1418 float *aLuxel, *bLuxel;
1421 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1422 if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1423 ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1428 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1429 a->brightness != b->brightness ||
1430 a->solid[ aNum ] != b->solid[ bNum ] ||
1431 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1435 /* compare solid color lightmaps */
1436 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1438 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1439 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1440 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1443 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1451 /* compare nonsolid lightmaps */
1452 if ( a->w != b->w || a->h != b->h ) {
1456 /* compare luxels */
1459 for ( y = 0; y < a->h; y++ )
1461 for ( x = 0; x < a->w; x++ )
1463 /* increment total */
1467 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1468 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1470 /* ignore unused luxels */
1471 if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1476 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1477 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1478 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1480 /* 2003-09-27: compare individual luxels */
1481 if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1485 /* compare (fixme: take into account perceptual differences) */
1486 delta += rd * LUXEL_COLOR_FRAC;
1487 delta += gd * LUXEL_COLOR_FRAC;
1488 delta += bd * LUXEL_COLOR_FRAC;
1490 /* is the change too high? */
1491 if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1497 /* made it this far, they must be identical (or close enough) */
1505 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1508 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1511 float luxel[ 3 ], *aLuxel, *bLuxel;
1515 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1516 a->brightness != b->brightness ||
1517 a->solid[ aNum ] != b->solid[ bNum ] ||
1518 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1522 /* compare solid lightmaps */
1523 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1525 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1526 VectorScale( luxel, 0.5f, luxel );
1529 VectorCopy( luxel, a->solidColor[ aNum ] );
1530 VectorCopy( luxel, b->solidColor[ bNum ] );
1532 /* return to sender */
1536 /* compare nonsolid lightmaps */
1537 if ( a->w != b->w || a->h != b->h ) {
1542 for ( y = 0; y < a->h; y++ )
1544 for ( x = 0; x < a->w; x++ )
1547 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1548 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1550 /* handle occlusion mismatch */
1551 if ( aLuxel[ 0 ] < 0.0f ) {
1552 VectorCopy( bLuxel, aLuxel );
1554 else if ( bLuxel[ 0 ] < 0.0f ) {
1555 VectorCopy( aLuxel, bLuxel );
1560 VectorAdd( aLuxel, bLuxel, luxel );
1561 VectorScale( luxel, 0.5f, luxel );
1563 /* debugging code */
1564 //% luxel[ 2 ] += 64.0f;
1567 VectorCopy( luxel, aLuxel );
1568 VectorCopy( luxel, bLuxel );
1581 determines if a single luxel is can be approximated with the interpolated vertex rgba
1584 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1585 int i, x, y, d, lightmapNum;
1587 vec3_t color, vertexColor;
1588 byte cb[ 4 ], vcb[ 4 ];
1591 /* find luxel xy coords */
1592 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1593 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1597 else if ( x >= lm->w ) {
1603 else if ( y >= lm->h ) {
1608 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1611 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1616 luxel = BSP_LUXEL( lightmapNum, x, y );
1618 /* ignore occluded luxels */
1619 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1623 /* copy, set min color and compare */
1624 VectorCopy( luxel, color );
1625 VectorCopy( dv->color[ 0 ], vertexColor );
1627 /* styles are not affected by minlight */
1628 if ( lightmapNum == 0 ) {
1629 for ( i = 0; i < 3; i++ )
1632 if ( color[ i ] < minLight[ i ] ) {
1633 color[ i ] = minLight[ i ];
1635 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1636 vertexColor[ i ] = minLight[ i ];
1642 ColorToBytes( color, cb, 1.0f );
1643 ColorToBytes( vertexColor, vcb, 1.0f );
1646 for ( i = 0; i < 3; i++ )
1648 d = cb[ i ] - vcb[ i ];
1652 if ( d > approximateTolerance ) {
1658 /* close enough for the girls i date */
1665 ApproximateTriangle()
1666 determines if a single triangle can be approximated with vertex rgba
1669 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1670 bspDrawVert_t mid, *dv2[ 3 ];
1674 /* approximate the vertexes */
1675 if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1678 if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1681 if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1685 /* subdivide calc */
1688 float dx, dy, dist, maxDist;
1691 /* find the longest edge and split it */
1694 for ( i = 0; i < 3; i++ )
1696 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1697 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1698 dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1699 if ( dist > maxDist ) {
1705 /* try to early out */
1706 if ( i < 0 || maxDist < subdivideThreshold ) {
1711 /* split the longest edge and map it */
1712 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1713 if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1717 /* recurse to first triangle */
1718 VectorCopy( dv, dv2 );
1720 if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1724 /* recurse to second triangle */
1725 VectorCopy( dv, dv2 );
1726 dv2[ ( max + 1 ) % 3 ] = ∣
1727 return ApproximateTriangle_r( lm, dv2 );
1733 ApproximateLightmap()
1734 determines if a raw lightmap can be approximated sufficiently with vertex colors
1737 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1738 int n, num, i, x, y, pw[ 5 ], r;
1739 bspDrawSurface_t *ds;
1740 surfaceInfo_t *info;
1741 mesh_t src, *subdivided, *mesh;
1742 bspDrawVert_t *verts, *dv[ 3 ];
1743 qboolean approximated;
1746 /* approximating? */
1747 if ( approximateTolerance <= 0 ) {
1751 /* test for jmonroe */
1753 /* don't approx lightmaps with styled twins */
1754 if ( lm->numStyledTwins > 0 ) {
1758 /* don't approx lightmaps with styles */
1759 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1761 if ( lm->styles[ i ] != LS_NONE ) {
1767 /* assume reduced until shadow detail is found */
1768 approximated = qtrue;
1770 /* walk the list of surfaces on this raw lightmap */
1771 for ( n = 0; n < lm->numLightSurfaces; n++ )
1774 num = lightSurfaces[ lm->firstLightSurface + n ];
1775 ds = &bspDrawSurfaces[ num ];
1776 info = &surfaceInfos[ num ];
1778 /* assume not-reduced initially */
1779 info->approximated = qfalse;
1781 /* bail if lightmap doesn't match up */
1782 if ( info->lm != lm ) {
1786 /* bail if not vertex lit */
1787 if ( info->si->noVertexLight ) {
1791 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1792 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1793 ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1794 ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1795 info->approximated = qtrue;
1796 numSurfsVertexForced++;
1800 /* handle the triangles */
1801 switch ( ds->surfaceType )
1805 verts = yDrawVerts + ds->firstVert;
1807 /* map the triangles */
1808 info->approximated = qtrue;
1809 for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1811 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1812 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1813 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1814 info->approximated = ApproximateTriangle_r( lm, dv );
1819 /* make a mesh from the drawsurf */
1820 src.width = ds->patchWidth;
1821 src.height = ds->patchHeight;
1822 src.verts = &yDrawVerts[ ds->firstVert ];
1823 //% subdivided = SubdivideMesh( src, 8, 512 );
1824 subdivided = SubdivideMesh2( src, info->patchIterations );
1826 /* fit it to the curve and remove colinear verts on rows/columns */
1827 PutMeshOnCurve( *subdivided );
1828 mesh = RemoveLinearMeshColumnsRows( subdivided );
1829 FreeMesh( subdivided );
1832 verts = mesh->verts;
1834 /* map the mesh quads */
1835 info->approximated = qtrue;
1836 for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1838 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1841 pw[ 0 ] = x + ( y * mesh->width );
1842 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1843 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1844 pw[ 3 ] = x + 1 + ( y * mesh->width );
1845 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1850 /* get drawverts and map first triangle */
1851 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1852 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1853 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1854 info->approximated = ApproximateTriangle_r( lm, dv );
1856 /* get drawverts and map second triangle */
1857 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1858 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1859 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1860 if ( info->approximated ) {
1861 info->approximated = ApproximateTriangle_r( lm, dv );
1875 if ( info->approximated == qfalse ) {
1876 approximated = qfalse;
1879 numSurfsVertexApproximated++;
1884 return approximated;
1890 TestOutLightmapStamp()
1891 tests a stamp on a given lightmap for validity
1894 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1895 int sx, sy, ox, oy, offset;
1900 if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1904 /* solid lightmaps test a 1x1 stamp */
1905 if ( lm->solid[ lightmapNum ] ) {
1906 offset = ( y * olm->customWidth ) + x;
1907 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1913 /* test the stamp */
1914 for ( sy = 0; sy < lm->h; sy++ )
1916 for ( sx = 0; sx < lm->w; sx++ )
1919 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1920 if ( luxel[ 0 ] < 0.0f ) {
1924 /* get bsp lightmap coords and test */
1927 offset = ( oy * olm->customWidth ) + ox;
1928 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1934 /* stamp is empty */
1942 sets up an output lightmap
1945 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1947 if ( lm == NULL || olm == NULL ) {
1951 /* is this a "normal" bsp-stored lightmap? */
1952 if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1953 olm->lightmapNum = numBSPLightmaps;
1956 /* lightmaps are interleaved with light direction maps */
1962 olm->lightmapNum = -3;
1965 /* set external lightmap number */
1966 olm->extLightmapNum = -1;
1969 olm->numLightmaps = 0;
1970 olm->customWidth = lm->customWidth;
1971 olm->customHeight = lm->customHeight;
1972 olm->freeLuxels = olm->customWidth * olm->customHeight;
1973 olm->numShaders = 0;
1975 /* allocate buffers */
1976 olm->lightBits = safe_malloc0( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1977 olm->bspLightBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
1979 olm->bspDirBytes = safe_malloc0( olm->customWidth * olm->customHeight * 3 );
1987 for a given surface lightmap, find output lightmap pages and positions for it
1990 #define LIGHTMAP_RESERVE_COUNT 1
1991 static void FindOutLightmaps( rawLightmap_t *lm, qboolean fastAllocate ){
1992 int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
1994 surfaceInfo_t *info;
1995 float *luxel, *deluxel;
1996 vec3_t color, direction;
1999 int xIncrement, yIncrement;
2001 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
2002 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2003 lm->outLightmapNums[ lightmapNum ] = -3;
2005 /* can this lightmap be approximated with vertex color? */
2006 if ( ApproximateLightmap( lm ) ) {
2011 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2014 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
2018 /* don't store twinned lightmaps */
2019 if ( lm->twins[ lightmapNum ] != NULL ) {
2023 /* if this is a styled lightmap, try some normalized locations first */
2025 if ( lightmapNum > 0 && outLightmaps != NULL ) {
2027 for ( j = 0; j < 2; j++ )
2029 /* try identical position */
2030 for ( i = 0; i < numOutLightmaps; i++ )
2032 /* get the output lightmap */
2033 olm = &outLightmaps[ i ];
2035 /* simple early out test */
2036 if ( olm->freeLuxels < lm->used ) {
2040 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2041 if ( olm->customWidth != lm->customWidth ||
2042 olm->customHeight != lm->customHeight ) {
2048 x = lm->lightmapX[ 0 ];
2049 y = lm->lightmapY[ 0 ];
2050 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2056 for ( sy = -1; sy <= 1; sy++ )
2058 for ( sx = -1; sx <= 1; sx++ )
2060 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
2061 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
2062 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2086 /* try normal placement algorithm */
2087 if ( ok == qfalse ) {
2092 /* walk the list of lightmap pages */
2093 if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
2097 i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
2099 for ( ; i < numOutLightmaps; i++ )
2101 /* get the output lightmap */
2102 olm = &outLightmaps[ i ];
2104 /* simple early out test */
2105 if ( olm->freeLuxels < lm->used ) {
2109 /* if fast allocation, skip lightmap files that are more than 90% complete */
2110 if ( fastAllocate == qtrue ) {
2111 if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
2116 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2117 if ( olm->customWidth != lm->customWidth ||
2118 olm->customHeight != lm->customHeight ) {
2123 if ( lm->solid[ lightmapNum ] ) {
2124 xMax = olm->customWidth;
2125 yMax = olm->customHeight;
2129 xMax = ( olm->customWidth - lm->w ) + 1;
2130 yMax = ( olm->customHeight - lm->h ) + 1;
2133 /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
2134 if ( fastAllocate == qtrue ) {
2135 xIncrement = MAX(1, lm->w / 15);
2136 yIncrement = MAX(1, lm->h / 15);
2143 /* walk the origin around the lightmap */
2144 for ( y = 0; y < yMax; y += yIncrement )
2146 for ( x = 0; x < xMax; x += xIncrement )
2148 /* find a fine tract of lauhnd */
2149 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2172 if ( ok == qfalse ) {
2173 /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2174 numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2175 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2177 Error( "FindOutLightmaps: Failed to allocate memory.\n" );
2180 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2181 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2182 free( outLightmaps );
2186 /* initialize both out lightmaps */
2187 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2188 SetupOutLightmap( lm, &outLightmaps[ k ] );
2190 /* set out lightmap */
2191 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2192 olm = &outLightmaps[ i ];
2194 /* set stamp xy origin to the first surface lightmap */
2195 if ( lightmapNum > 0 ) {
2196 x = lm->lightmapX[ 0 ];
2197 y = lm->lightmapY[ 0 ];
2201 /* if this is a style-using lightmap, it must be exported */
2202 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2203 olm->extLightmapNum = 0;
2206 /* add the surface lightmap to the bsp lightmap */
2207 lm->outLightmapNums[ lightmapNum ] = i;
2208 lm->lightmapX[ lightmapNum ] = x;
2209 lm->lightmapY[ lightmapNum ] = y;
2210 olm->numLightmaps++;
2213 for ( i = 0; i < lm->numLightSurfaces; i++ )
2215 /* get surface info */
2216 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2218 /* test for shader */
2219 for ( j = 0; j < olm->numShaders; j++ )
2221 if ( olm->shaders[ j ] == info->si ) {
2226 /* if it doesn't exist, add it */
2227 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2228 olm->shaders[ olm->numShaders ] = info->si;
2230 numLightmapShaders++;
2235 if ( lm->solid[ lightmapNum ] ) {
2245 /* mark the bits used */
2246 for ( y = 0; y < yMax; y++ )
2248 for ( x = 0; x < xMax; x++ )
2251 luxel = BSP_LUXEL( lightmapNum, x, y );
2252 deluxel = BSP_DELUXEL( x, y );
2253 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2257 /* set minimum light */
2258 if ( lm->solid[ lightmapNum ] ) {
2260 VectorSet( color, 255.0f, 0.0f, 0.0f );
2263 VectorCopy( lm->solidColor[ lightmapNum ], color );
2267 VectorCopy( luxel, color );
2270 /* styles are not affected by minlight */
2271 if ( lightmapNum == 0 ) {
2272 for ( i = 0; i < 3; i++ )
2274 if ( color[ i ] < minLight[ i ] ) {
2275 color[ i ] = minLight[ i ];
2280 /* get bsp lightmap coords */
2281 ox = x + lm->lightmapX[ lightmapNum ];
2282 oy = y + lm->lightmapY[ lightmapNum ];
2283 offset = ( oy * olm->customWidth ) + ox;
2285 /* flag pixel as used */
2286 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2290 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2291 ColorToBytes( color, pixel, lm->brightness );
2293 /* store direction */
2295 /* normalize average light direction */
2296 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2297 VectorScale( deluxel, 1000.0f, direction );
2298 VectorNormalize( direction, direction );
2299 VectorScale( direction, 127.5f, direction );
2300 for ( i = 0; i < 3; i++ )
2301 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2311 CompareRawLightmap()
2312 compare function for qsort()
2315 static int CompareRawLightmap( const void *a, const void *b ){
2316 rawLightmap_t *alm, *blm;
2317 surfaceInfo_t *aInfo, *bInfo;
2322 alm = &rawLightmaps[ *( (const int*) a ) ];
2323 blm = &rawLightmaps[ *( (const int*) b ) ];
2325 /* get min number of surfaces */
2326 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2329 for ( i = 0; i < min; i++ )
2331 /* get surface info */
2332 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2333 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2335 /* compare shader names */
2336 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2342 /* test style count */
2344 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2345 diff += blm->styles[ i ] - alm->styles[ i ];
2351 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2356 /* must be equivalent */
2362 void FillOutLightmap( outLightmap_t *olm ){
2365 vec3_t dir_sum, light_sum;
2367 byte *lightBitsNew = NULL;
2368 byte *lightBytesNew = NULL;
2369 byte *dirBytesNew = NULL;
2371 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2372 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2374 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2378 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2379 olm->lightBits[0] |= 1;
2380 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2381 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2382 olm->bspLightBytes[0] = 255;
2383 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2386 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2387 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2389 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2395 for ( y = 0; y < olm->customHeight; ++y )
2397 for ( x = 0; x < olm->customWidth; ++x )
2399 ofs = y * olm->customWidth + x;
2400 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2404 VectorClear( dir_sum );
2405 VectorClear( light_sum );
2407 /* try all four neighbors */
2408 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2409 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2411 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2413 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2417 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2418 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2420 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2422 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2426 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2427 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2429 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2431 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2435 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2436 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2438 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2440 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2446 ofs = y * olm->customWidth + x;
2447 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2448 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2450 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2460 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2461 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2463 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2467 free( lightBitsNew );
2468 free( lightBytesNew );
2470 free( dirBytesNew );
2477 StoreSurfaceLightmaps()
2478 stores the surface lightmaps into the bsp as byte rgb triplets
2481 void StoreSurfaceLightmaps( qboolean fastAllocate, qboolean storeForReal ){
2482 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2483 int style, size, lightmapNum, lightmapNum2;
2484 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2485 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2486 float *deluxel, *bspDeluxel, *bspDeluxel2;
2488 int numUsed, numTwins, numTwinLuxels, numStored;
2489 float lmx, lmy, efficiency;
2491 bspDrawSurface_t *ds, *parent, dsTemp;
2492 surfaceInfo_t *info;
2493 rawLightmap_t *lm, *lm2;
2495 bspDrawVert_t *dv, *ydv, *dvParent;
2496 char dirname[ 1024 ], filename[ 1024 ];
2498 char lightmapName[ 128 ];
2499 const char *rgbGenValues[ 256 ];
2500 const char *alphaGenValues[ 256 ];
2504 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2507 if ( lmCustomDir ) {
2508 strcpy( dirname, lmCustomDir );
2512 strcpy( dirname, source );
2513 StripExtension( dirname );
2515 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2516 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2518 /* -----------------------------------------------------------------
2519 average the sampled luxels into the bsp luxels
2520 ----------------------------------------------------------------- */
2523 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2525 /* walk the list of raw lightmaps */
2529 numSolidLightmaps = 0;
2530 for ( i = 0; i < numRawLightmaps; i++ )
2533 lm = &rawLightmaps[ i ];
2535 /* walk individual lightmaps */
2536 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2539 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2543 /* allocate bsp luxel storage */
2544 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2545 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2546 lm->bspLuxels[ lightmapNum ] = safe_malloc0( size );
2549 /* allocate radiosity lightmap storage */
2551 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2552 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2553 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2555 memset( lm->radLuxels[ lightmapNum ], 0, size );
2558 /* average supersampled luxels */
2559 for ( y = 0; y < lm->h; y++ )
2561 for ( x = 0; x < lm->w; x++ )
2565 occludedSamples = 0.0f;
2567 VectorClear( sample );
2568 VectorClear( occludedSample );
2569 VectorClear( dirSample );
2570 for ( ly = 0; ly < superSample; ly++ )
2572 for ( lx = 0; lx < superSample; lx++ )
2575 sx = x * superSample + lx;
2576 sy = y * superSample + ly;
2577 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2578 deluxel = SUPER_DELUXEL( sx, sy );
2579 normal = SUPER_NORMAL( sx, sy );
2580 cluster = SUPER_CLUSTER( sx, sy );
2582 /* sample deluxemap */
2583 if ( deluxemap && lightmapNum == 0 ) {
2584 VectorAdd( dirSample, deluxel, dirSample );
2587 /* keep track of used/occluded samples */
2588 if ( *cluster != CLUSTER_UNMAPPED ) {
2592 /* handle lightmap border? */
2593 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2594 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2599 else if ( debug && *cluster < 0 ) {
2600 if ( *cluster == CLUSTER_UNMAPPED ) {
2601 VectorSet( luxel, 255, 204, 0 );
2603 else if ( *cluster == CLUSTER_OCCLUDED ) {
2604 VectorSet( luxel, 255, 0, 255 );
2606 else if ( *cluster == CLUSTER_FLOODED ) {
2607 VectorSet( luxel, 0, 32, 255 );
2609 VectorAdd( occludedSample, luxel, occludedSample );
2610 occludedSamples += 1.0f;
2613 /* normal luxel handling */
2614 else if ( luxel[ 3 ] > 0.0f ) {
2615 /* handle lit or flooded luxels */
2616 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2617 VectorAdd( sample, luxel, sample );
2618 samples += luxel[ 3 ];
2621 /* handle occluded or unmapped luxels */
2624 VectorAdd( occludedSample, luxel, occludedSample );
2625 occludedSamples += luxel[ 3 ];
2628 /* handle style debugging */
2629 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2630 VectorCopy( debugColors[ 0 ], sample );
2637 /* only use occluded samples if necessary */
2638 if ( samples <= 0.0f ) {
2639 VectorCopy( occludedSample, sample );
2640 samples = occludedSamples;
2644 luxel = SUPER_LUXEL( lightmapNum, x, y );
2645 deluxel = SUPER_DELUXEL( x, y );
2647 /* store light direction */
2648 if ( deluxemap && lightmapNum == 0 ) {
2649 VectorCopy( dirSample, deluxel );
2652 /* store the sample back in super luxels */
2653 if ( samples > 0.01f ) {
2654 VectorScale( sample, ( 1.0f / samples ), luxel );
2658 /* if any samples were mapped in any way, store ambient color */
2659 else if ( mappedSamples > 0 ) {
2660 if ( lightmapNum == 0 ) {
2661 VectorCopy( ambientColor, luxel );
2664 VectorClear( luxel );
2669 /* store a bogus value to be fixed later */
2672 VectorClear( luxel );
2680 ClearBounds( colorMins, colorMaxs );
2682 /* clean up and store into bsp luxels */
2683 for ( y = 0; y < lm->h; y++ )
2685 for ( x = 0; x < lm->w; x++ )
2688 luxel = SUPER_LUXEL( lightmapNum, x, y );
2689 deluxel = SUPER_DELUXEL( x, y );
2691 /* copy light direction */
2692 if ( deluxemap && lightmapNum == 0 ) {
2693 VectorCopy( deluxel, dirSample );
2696 /* is this a valid sample? */
2697 if ( luxel[ 3 ] > 0.0f ) {
2698 VectorCopy( luxel, sample );
2699 samples = luxel[ 3 ];
2703 /* fix negative samples */
2704 for ( j = 0; j < 3; j++ )
2706 if ( sample[ j ] < 0.0f ) {
2713 /* nick an average value from the neighbors */
2714 VectorClear( sample );
2715 VectorClear( dirSample );
2718 /* fixme: why is this disabled?? */
2719 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2721 if ( sy < 0 || sy >= lm->h ) {
2725 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2727 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2731 /* get neighbor's particulars */
2732 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2733 if ( luxel[ 3 ] < 0.0f ) {
2736 VectorAdd( sample, luxel, sample );
2737 samples += luxel[ 3 ];
2742 if ( samples == 0.0f ) {
2743 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2751 /* fix negative samples */
2752 for ( j = 0; j < 3; j++ )
2754 if ( sample[ j ] < 0.0f ) {
2761 /* scale the sample */
2762 VectorScale( sample, ( 1.0f / samples ), sample );
2764 /* store the sample in the radiosity luxels */
2766 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2767 VectorCopy( sample, radLuxel );
2769 /* if only storing bounced light, early out here */
2770 if ( bounceOnly && !bouncing ) {
2775 /* store the sample in the bsp luxels */
2776 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2777 bspDeluxel = BSP_DELUXEL( x, y );
2779 VectorAdd( bspLuxel, sample, bspLuxel );
2780 if ( deluxemap && lightmapNum == 0 ) {
2781 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2784 /* add color to bounds for solid checking */
2785 if ( samples > 0.0f ) {
2786 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2791 /* set solid color */
2792 lm->solid[ lightmapNum ] = qfalse;
2793 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2794 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2796 /* nocollapse prevents solid lightmaps */
2797 if ( noCollapse == qfalse ) {
2798 /* check solid color */
2799 VectorSubtract( colorMaxs, colorMins, sample );
2800 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2801 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2803 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2804 lm->solid[ lightmapNum ] = qtrue;
2805 numSolidLightmaps++;
2808 /* if all lightmaps aren't solid, then none of them are solid */
2809 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2810 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2812 if ( lm->solid[ y ] ) {
2813 numSolidLightmaps--;
2815 lm->solid[ y ] = qfalse;
2820 /* wrap bsp luxels if necessary */
2821 if ( lm->wrap[ 0 ] ) {
2822 for ( y = 0; y < lm->h; y++ )
2824 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2825 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2826 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2827 VectorScale( bspLuxel, 0.5f, bspLuxel );
2828 VectorCopy( bspLuxel, bspLuxel2 );
2829 if ( deluxemap && lightmapNum == 0 ) {
2830 bspDeluxel = BSP_DELUXEL( 0, y );
2831 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2832 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2833 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2834 VectorCopy( bspDeluxel, bspDeluxel2 );
2838 if ( lm->wrap[ 1 ] ) {
2839 for ( x = 0; x < lm->w; x++ )
2841 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2842 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2843 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2844 VectorScale( bspLuxel, 0.5f, bspLuxel );
2845 VectorCopy( bspLuxel, bspLuxel2 );
2846 if ( deluxemap && lightmapNum == 0 ) {
2847 bspDeluxel = BSP_DELUXEL( x, 0 );
2848 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2849 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2850 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2851 VectorCopy( bspDeluxel, bspDeluxel2 );
2858 /* -----------------------------------------------------------------
2859 convert modelspace deluxemaps to tangentspace
2860 ----------------------------------------------------------------- */
2863 if ( deluxemap && deluxemode == 1 ) {
2864 vec3_t worldUp, myNormal, myTangent, myBinormal;
2867 Sys_Printf( "converting..." );
2869 for ( i = 0; i < numRawLightmaps; i++ )
2872 lm = &rawLightmaps[ i ];
2874 /* walk lightmap samples */
2875 for ( y = 0; y < lm->sh; y++ )
2877 for ( x = 0; x < lm->sw; x++ )
2879 /* get normal and deluxel */
2880 normal = SUPER_NORMAL( x, y );
2881 cluster = SUPER_CLUSTER( x, y );
2882 bspDeluxel = BSP_DELUXEL( x, y );
2883 deluxel = SUPER_DELUXEL( x, y );
2886 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2888 /* get tangent vectors */
2889 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2890 if ( myNormal[ 2 ] == 1.0f ) {
2891 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2892 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2894 else if ( myNormal[ 2 ] == -1.0f ) {
2895 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2896 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2901 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2902 CrossProduct( myNormal, worldUp, myTangent );
2903 VectorNormalize( myTangent, myTangent );
2904 CrossProduct( myTangent, myNormal, myBinormal );
2905 VectorNormalize( myBinormal, myBinormal );
2908 /* project onto plane */
2909 dist = -DotProduct( myTangent, myNormal );
2910 VectorMA( myTangent, dist, myNormal, myTangent );
2911 dist = -DotProduct( myBinormal, myNormal );
2912 VectorMA( myBinormal, dist, myNormal, myBinormal );
2915 VectorNormalize( myTangent, myTangent );
2916 VectorNormalize( myBinormal, myBinormal );
2918 /* convert modelspace deluxel to tangentspace */
2919 dirSample[0] = bspDeluxel[0];
2920 dirSample[1] = bspDeluxel[1];
2921 dirSample[2] = bspDeluxel[2];
2922 VectorNormalize( dirSample, dirSample );
2924 /* fix tangents to world matrix */
2925 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2926 VectorNegate( myTangent, myTangent );
2929 /* build tangentspace vectors */
2930 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2931 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2932 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2939 /* -----------------------------------------------------------------
2941 ----------------------------------------------------------------- */
2943 #ifdef sdfsdfwq312323
2945 Sys_Printf( "blending..." );
2947 for ( i = 0; i < numRawLightmaps; i++ )
2953 lm = &rawLightmaps[ i ];
2955 /* walk individual lightmaps */
2956 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2959 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2963 /* walk lightmap samples */
2964 for ( y = 0; y < lm->sh; y++ )
2966 for ( x = 0; x < lm->sw; x++ )
2969 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2972 VectorNormalize( bspLuxel, myColor );
2973 myBrightness = VectorLength( bspLuxel );
2974 myBrightness *= ( 1 / 127.0f );
2975 myBrightness = myBrightness * myBrightness;
2976 myBrightness *= 127.0f;
2977 VectorScale( myColor, myBrightness, bspLuxel );
2984 /* -----------------------------------------------------------------
2985 collapse non-unique lightmaps
2986 ----------------------------------------------------------------- */
2988 if ( storeForReal && noCollapse == qfalse && deluxemap == qfalse ) {
2990 Sys_FPrintf( SYS_VRB, "collapsing..." );
2992 /* set all twin refs to null */
2993 for ( i = 0; i < numRawLightmaps; i++ )
2995 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2997 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2998 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2999 rawLightmaps[ i ].numStyledTwins = 0;
3003 /* walk the list of raw lightmaps */
3004 for ( i = 0; i < numRawLightmaps; i++ )
3007 lm = &rawLightmaps[ i ];
3009 /* walk lightmaps */
3010 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3013 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
3014 lm->twins[ lightmapNum ] != NULL ) {
3018 /* find all lightmaps that are virtually identical to this one */
3019 for ( j = i + 1; j < numRawLightmaps; j++ )
3022 lm2 = &rawLightmaps[ j ];
3024 /* walk lightmaps */
3025 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3028 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3029 lm2->twins[ lightmapNum2 ] != NULL ) {
3034 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3035 /* merge and set twin */
3036 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3037 lm2->twins[ lightmapNum2 ] = lm;
3038 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3040 numTwinLuxels += ( lm->w * lm->h );
3042 /* count styled twins */
3043 if ( lightmapNum > 0 ) {
3044 lm->numStyledTwins++;
3054 /* -----------------------------------------------------------------
3055 sort raw lightmaps by shader
3056 ----------------------------------------------------------------- */
3059 Sys_FPrintf( SYS_VRB, "sorting..." );
3061 /* allocate a new sorted list */
3062 if ( sortLightmaps == NULL ) {
3063 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3066 /* fill it out and sort it */
3067 for ( i = 0; i < numRawLightmaps; i++ )
3068 sortLightmaps[ i ] = i;
3069 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3071 /* -----------------------------------------------------------------
3072 allocate output lightmaps
3073 ----------------------------------------------------------------- */
3075 if ( storeForReal ) {
3077 Sys_FPrintf( SYS_VRB, "allocating..." );
3079 /* kill all existing output lightmaps */
3080 if ( outLightmaps != NULL ) {
3081 for ( i = 0; i < numOutLightmaps; i++ )
3083 free( outLightmaps[ i ].lightBits );
3084 free( outLightmaps[ i ].bspLightBytes );
3086 free( outLightmaps );
3087 outLightmaps = NULL;
3090 numLightmapShaders = 0;
3091 numOutLightmaps = 0;
3092 numBSPLightmaps = 0;
3093 numExtLightmaps = 0;
3095 /* find output lightmap */
3096 for ( i = 0; i < numRawLightmaps; i++ )
3098 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3099 FindOutLightmaps( lm, fastAllocate );
3102 /* set output numbers in twinned lightmaps */
3103 for ( i = 0; i < numRawLightmaps; i++ )
3106 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3108 /* walk lightmaps */
3109 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3112 lm2 = lm->twins[ lightmapNum ];
3113 if ( lm2 == NULL ) {
3116 lightmapNum2 = lm->twinNums[ lightmapNum ];
3118 /* find output lightmap from twin */
3119 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3120 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3121 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3126 /* -----------------------------------------------------------------
3127 store output lightmaps
3128 ----------------------------------------------------------------- */
3130 if ( storeForReal ) {
3132 Sys_FPrintf( SYS_VRB, "storing..." );
3134 /* count the bsp lightmaps and allocate space */
3135 if ( bspLightBytes != NULL ) {
3136 free( bspLightBytes );
3138 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3139 numBSPLightBytes = 0;
3140 bspLightBytes = NULL;
3144 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3145 bspLightBytes = safe_malloc0( numBSPLightBytes );
3148 /* walk the list of output lightmaps */
3149 for ( i = 0; i < numOutLightmaps; i++ )
3151 /* get output lightmap */
3152 olm = &outLightmaps[ i ];
3154 /* fill output lightmap */
3155 if ( lightmapFill ) {
3156 FillOutLightmap( olm );
3159 /* is this a valid bsp lightmap? */
3160 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3161 /* copy lighting data */
3162 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3163 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3165 /* copy direction data */
3167 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3168 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3172 /* external lightmap? */
3173 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3174 /* make a directory for the lightmaps */
3177 /* set external lightmap number */
3178 olm->extLightmapNum = numExtLightmaps;
3180 /* write lightmap */
3181 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3182 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3183 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3186 /* write deluxemap */
3188 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3189 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3190 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3193 if ( debugDeluxemap ) {
3194 olm->extLightmapNum++;
3200 if ( numExtLightmaps > 0 ) {
3201 Sys_FPrintf( SYS_VRB, "\n" );
3204 /* delete unused external lightmaps */
3205 for ( i = numExtLightmaps; i; i++ )
3207 /* determine if file exists */
3208 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3209 if ( !FileExists( filename ) ) {
3218 /* -----------------------------------------------------------------
3219 project the lightmaps onto the bsp surfaces
3220 ----------------------------------------------------------------- */
3222 if ( storeForReal ) {
3224 Sys_FPrintf( SYS_VRB, "projecting..." );
3226 /* walk the list of surfaces */
3227 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3229 /* get the surface and info */
3230 ds = &bspDrawSurfaces[ i ];
3231 info = &surfaceInfos[ i ];
3235 /* handle surfaces with identical parent */
3236 if ( info->parentSurfaceNum >= 0 ) {
3237 /* preserve original data and get parent */
3238 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3239 memcpy( &dsTemp, ds, sizeof( *ds ) );
3241 /* overwrite child with parent data */
3242 memcpy( ds, parent, sizeof( *ds ) );
3244 /* restore key parts */
3245 ds->fogNum = dsTemp.fogNum;
3246 ds->firstVert = dsTemp.firstVert;
3247 ds->firstIndex = dsTemp.firstIndex;
3248 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3250 /* set vertex data */
3251 dv = &bspDrawVerts[ ds->firstVert ];
3252 dvParent = &bspDrawVerts[ parent->firstVert ];
3253 for ( j = 0; j < ds->numVerts; j++ )
3255 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3256 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3263 /* handle vertex lit or approximated surfaces */
3264 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3265 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3267 ds->lightmapNum[ lightmapNum ] = -3;
3268 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3272 /* handle lightmapped surfaces */
3275 /* walk lightmaps */
3276 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3279 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3281 /* handle unused style */
3282 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3283 ds->lightmapNum[ lightmapNum ] = -3;
3287 /* get output lightmap */
3288 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3290 /* set bsp lightmap number */
3291 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3293 /* deluxemap debugging makes the deluxemap visible */
3294 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3295 ds->lightmapNum[ lightmapNum ]++;
3298 /* calc lightmap origin in texture space */
3299 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3300 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3302 /* calc lightmap st coords */
3303 dv = &bspDrawVerts[ ds->firstVert ];
3304 ydv = &yDrawVerts[ ds->firstVert ];
3305 for ( j = 0; j < ds->numVerts; j++ )
3307 if ( lm->solid[ lightmapNum ] ) {
3308 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3309 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3313 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3314 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3320 /* store vertex colors */
3321 dv = &bspDrawVerts[ ds->firstVert ];
3322 for ( j = 0; j < ds->numVerts; j++ )
3324 /* walk lightmaps */
3325 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3327 /* handle unused style */
3328 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3329 VectorClear( color );
3333 /* get vertex color */
3334 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3335 VectorCopy( luxel, color );
3337 /* set minimum light */
3338 if ( lightmapNum == 0 ) {
3339 for ( k = 0; k < 3; k++ )
3340 if ( color[ k ] < minVertexLight[ k ] ) {
3341 color[ k ] = minVertexLight[ k ];
3346 /* store to bytes */
3347 if ( !info->si->noVertexLight ) {
3348 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3353 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3354 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3356 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3360 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3361 dv = &bspDrawVerts[ ds->firstVert ];
3363 /* depthFunc equal? */
3364 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3371 /* generate stages for styled lightmaps */
3372 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3375 style = lm->styles[ lightmapNum ];
3376 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3380 /* get output lightmap */
3381 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3384 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3385 strcpy( lightmapName, "$lightmap" );
3388 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3391 /* get rgbgen string */
3392 if ( rgbGenValues[ style ] == NULL ) {
3393 sprintf( key, "_style%drgbgen", style );
3394 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3395 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3396 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3400 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3401 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3407 /* get alphagen string */
3408 if ( alphaGenValues[ style ] == NULL ) {
3409 sprintf( key, "_style%dalphagen", style );
3410 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3412 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3413 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3416 alphaGen[ 0 ] = '\0';
3419 /* calculate st offset */
3420 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3421 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3423 /* create additional stage */
3424 if ( lmx == 0.0f && lmy == 0.0f ) {
3425 sprintf( styleStage, "\t{\n"
3426 "\t\tmap %s\n" /* lightmap */
3427 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3428 "%s" /* depthFunc equal */
3431 "\t\ttcGen lightmap\n"
3434 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3440 sprintf( styleStage, "\t{\n"
3441 "\t\tmap %s\n" /* lightmap */
3442 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3443 "%s" /* depthFunc equal */
3446 "\t\ttcGen lightmap\n"
3447 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3450 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3458 strcat( styleStages, styleStage );
3461 /* create custom shader */
3462 if ( info->si->styleMarker == 2 ) {
3463 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3466 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3469 /* emit remap command */
3470 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3473 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3474 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3475 //% Sys_Printf( ")\n" );
3478 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3479 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3480 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3481 /* get output lightmap */
3482 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3484 /* do some name mangling */
3485 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3487 /* create custom shader */
3488 csi = CustomShader( info->si, "$lightmap", lightmapName );
3491 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3492 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3493 //% Sys_Printf( ")\n" );
3496 /* use the normal plain-jane shader */
3498 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3504 Sys_FPrintf( SYS_VRB, "done.\n" );
3506 /* calc num stored */
3507 numStored = numBSPLightBytes / 3;
3508 efficiency = ( numStored <= 0 )
3510 : (float) numUsed / (float) numStored;
3512 if ( storeForReal ) {
3514 Sys_Printf( "%9d luxels used\n", numUsed );
3515 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3516 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3517 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3518 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3519 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3520 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3521 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3522 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3524 /* write map shader file */
3525 WriteMapShaderFile();