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 );
2328 //#define allocate_bigger_first
2329 #ifdef allocate_bigger_first
2330 /* compare size, allocate bigger first */
2331 // fastAllocate commit part: can kick fps by unique lightmap/shader combinations*=~2 + bigger compile time
2332 //return -diff; makes packing faster and rough
2333 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2339 for ( i = 0; i < min; i++ )
2341 /* get surface info */
2342 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2343 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2345 /* compare shader names */
2346 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2352 /* test style count */
2354 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2355 diff += blm->styles[ i ] - alm->styles[ i ];
2359 #ifndef allocate_bigger_first
2361 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2366 /* must be equivalent */
2372 void FillOutLightmap( outLightmap_t *olm ){
2375 vec3_t dir_sum, light_sum;
2377 byte *lightBitsNew = NULL;
2378 byte *lightBytesNew = NULL;
2379 byte *dirBytesNew = NULL;
2381 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2382 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2384 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2388 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2389 olm->lightBits[0] |= 1;
2390 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2391 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2392 olm->bspLightBytes[0] = 255;
2393 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2396 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2397 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2399 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2405 for ( y = 0; y < olm->customHeight; ++y )
2407 for ( x = 0; x < olm->customWidth; ++x )
2409 ofs = y * olm->customWidth + x;
2410 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2414 VectorClear( dir_sum );
2415 VectorClear( light_sum );
2417 /* try all four neighbors */
2418 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2419 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2421 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2423 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2427 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2428 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2430 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2432 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2436 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2437 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2439 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2441 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2445 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2446 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2448 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2450 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2456 ofs = y * olm->customWidth + x;
2457 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2458 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2460 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2470 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2471 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2473 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2477 free( lightBitsNew );
2478 free( lightBytesNew );
2480 free( dirBytesNew );
2485 StoreSurfaceLightmaps()
2486 stores the surface lightmaps into the bsp as byte rgb triplets
2489 void StoreSurfaceLightmaps( qboolean fastAllocate, qboolean storeForReal ){
2490 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples, timer_start;
2491 int style, size, lightmapNum, lightmapNum2;
2492 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2493 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2494 float *deluxel, *bspDeluxel, *bspDeluxel2;
2496 int numUsed, numTwins, numTwinLuxels, numStored;
2497 float lmx, lmy, efficiency;
2499 bspDrawSurface_t *ds, *parent, dsTemp;
2500 surfaceInfo_t *info;
2501 rawLightmap_t *lm, *lm2;
2503 bspDrawVert_t *dv, *ydv, *dvParent;
2504 char dirname[ 1024 ], filename[ 1024 ];
2506 char lightmapName[ 128 ];
2507 const char *rgbGenValues[ 256 ];
2508 const char *alphaGenValues[ 256 ];
2512 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2515 if ( lmCustomDir ) {
2516 strcpy( dirname, lmCustomDir );
2520 strcpy( dirname, source );
2521 StripExtension( dirname );
2523 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2524 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2526 /* -----------------------------------------------------------------
2527 average the sampled luxels into the bsp luxels
2528 ----------------------------------------------------------------- */
2531 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2533 timer_start = I_FloatTime();
2535 /* walk the list of raw lightmaps */
2539 numSolidLightmaps = 0;
2540 for ( i = 0; i < numRawLightmaps; i++ )
2543 lm = &rawLightmaps[ i ];
2545 /* walk individual lightmaps */
2546 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2549 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2553 /* allocate bsp luxel storage */
2554 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2555 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2556 lm->bspLuxels[ lightmapNum ] = safe_malloc0( size );
2559 /* allocate radiosity lightmap storage */
2561 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2562 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2563 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2565 memset( lm->radLuxels[ lightmapNum ], 0, size );
2568 /* average supersampled luxels */
2569 for ( y = 0; y < lm->h; y++ )
2571 for ( x = 0; x < lm->w; x++ )
2575 occludedSamples = 0.0f;
2577 VectorClear( sample );
2578 VectorClear( occludedSample );
2579 VectorClear( dirSample );
2580 for ( ly = 0; ly < superSample; ly++ )
2582 for ( lx = 0; lx < superSample; lx++ )
2585 sx = x * superSample + lx;
2586 sy = y * superSample + ly;
2587 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2588 deluxel = SUPER_DELUXEL( sx, sy );
2589 normal = SUPER_NORMAL( sx, sy );
2590 cluster = SUPER_CLUSTER( sx, sy );
2592 /* sample deluxemap */
2593 if ( deluxemap && lightmapNum == 0 ) {
2594 VectorAdd( dirSample, deluxel, dirSample );
2597 /* keep track of used/occluded samples */
2598 if ( *cluster != CLUSTER_UNMAPPED ) {
2602 /* handle lightmap border? */
2603 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2604 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2609 else if ( debug && *cluster < 0 ) {
2610 if ( *cluster == CLUSTER_UNMAPPED ) {
2611 VectorSet( luxel, 255, 204, 0 );
2613 else if ( *cluster == CLUSTER_OCCLUDED ) {
2614 VectorSet( luxel, 255, 0, 255 );
2616 else if ( *cluster == CLUSTER_FLOODED ) {
2617 VectorSet( luxel, 0, 32, 255 );
2619 VectorAdd( occludedSample, luxel, occludedSample );
2620 occludedSamples += 1.0f;
2623 /* normal luxel handling */
2624 else if ( luxel[ 3 ] > 0.0f ) {
2625 /* handle lit or flooded luxels */
2626 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2627 VectorAdd( sample, luxel, sample );
2628 samples += luxel[ 3 ];
2631 /* handle occluded or unmapped luxels */
2634 VectorAdd( occludedSample, luxel, occludedSample );
2635 occludedSamples += luxel[ 3 ];
2638 /* handle style debugging */
2639 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2640 VectorCopy( debugColors[ 0 ], sample );
2647 /* only use occluded samples if necessary */
2648 if ( samples <= 0.0f ) {
2649 VectorCopy( occludedSample, sample );
2650 samples = occludedSamples;
2654 luxel = SUPER_LUXEL( lightmapNum, x, y );
2655 deluxel = SUPER_DELUXEL( x, y );
2657 /* store light direction */
2658 if ( deluxemap && lightmapNum == 0 ) {
2659 VectorCopy( dirSample, deluxel );
2662 /* store the sample back in super luxels */
2663 if ( samples > 0.01f ) {
2664 VectorScale( sample, ( 1.0f / samples ), luxel );
2668 /* if any samples were mapped in any way, store ambient color */
2669 else if ( mappedSamples > 0 ) {
2670 if ( lightmapNum == 0 ) {
2671 VectorCopy( ambientColor, luxel );
2674 VectorClear( luxel );
2679 /* store a bogus value to be fixed later */
2682 VectorClear( luxel );
2690 ClearBounds( colorMins, colorMaxs );
2692 /* clean up and store into bsp luxels */
2693 for ( y = 0; y < lm->h; y++ )
2695 for ( x = 0; x < lm->w; x++ )
2698 luxel = SUPER_LUXEL( lightmapNum, x, y );
2699 deluxel = SUPER_DELUXEL( x, y );
2701 /* copy light direction */
2702 if ( deluxemap && lightmapNum == 0 ) {
2703 VectorCopy( deluxel, dirSample );
2706 /* is this a valid sample? */
2707 if ( luxel[ 3 ] > 0.0f ) {
2708 VectorCopy( luxel, sample );
2709 samples = luxel[ 3 ];
2713 /* fix negative samples */
2714 for ( j = 0; j < 3; j++ )
2716 if ( sample[ j ] < 0.0f ) {
2723 /* nick an average value from the neighbors */
2724 VectorClear( sample );
2725 VectorClear( dirSample );
2728 /* fixme: why is this disabled?? */
2729 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2731 if ( sy < 0 || sy >= lm->h ) {
2735 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2737 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2741 /* get neighbor's particulars */
2742 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2743 if ( luxel[ 3 ] < 0.0f ) {
2746 VectorAdd( sample, luxel, sample );
2747 samples += luxel[ 3 ];
2752 if ( samples == 0.0f ) {
2753 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2761 /* fix negative samples */
2762 for ( j = 0; j < 3; j++ )
2764 if ( sample[ j ] < 0.0f ) {
2771 /* scale the sample */
2772 VectorScale( sample, ( 1.0f / samples ), sample );
2774 /* store the sample in the radiosity luxels */
2776 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2777 VectorCopy( sample, radLuxel );
2779 /* if only storing bounced light, early out here */
2780 if ( bounceOnly && !bouncing ) {
2785 /* store the sample in the bsp luxels */
2786 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2787 bspDeluxel = BSP_DELUXEL( x, y );
2789 VectorAdd( bspLuxel, sample, bspLuxel );
2790 if ( deluxemap && lightmapNum == 0 ) {
2791 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2794 /* add color to bounds for solid checking */
2795 if ( samples > 0.0f ) {
2796 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2801 /* set solid color */
2802 lm->solid[ lightmapNum ] = qfalse;
2803 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2804 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2806 /* nocollapse prevents solid lightmaps */
2807 if ( noCollapse == qfalse ) {
2808 /* check solid color */
2809 VectorSubtract( colorMaxs, colorMins, sample );
2810 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2811 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2813 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2814 lm->solid[ lightmapNum ] = qtrue;
2815 numSolidLightmaps++;
2818 /* if all lightmaps aren't solid, then none of them are solid */
2819 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2820 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2822 if ( lm->solid[ y ] ) {
2823 numSolidLightmaps--;
2825 lm->solid[ y ] = qfalse;
2830 /* wrap bsp luxels if necessary */
2831 if ( lm->wrap[ 0 ] ) {
2832 for ( y = 0; y < lm->h; y++ )
2834 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2835 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2836 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2837 VectorScale( bspLuxel, 0.5f, bspLuxel );
2838 VectorCopy( bspLuxel, bspLuxel2 );
2839 if ( deluxemap && lightmapNum == 0 ) {
2840 bspDeluxel = BSP_DELUXEL( 0, y );
2841 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2842 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2843 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2844 VectorCopy( bspDeluxel, bspDeluxel2 );
2848 if ( lm->wrap[ 1 ] ) {
2849 for ( x = 0; x < lm->w; x++ )
2851 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2852 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2853 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2854 VectorScale( bspLuxel, 0.5f, bspLuxel );
2855 VectorCopy( bspLuxel, bspLuxel2 );
2856 if ( deluxemap && lightmapNum == 0 ) {
2857 bspDeluxel = BSP_DELUXEL( x, 0 );
2858 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2859 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2860 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2861 VectorCopy( bspDeluxel, bspDeluxel2 );
2868 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
2870 /* -----------------------------------------------------------------
2871 convert modelspace deluxemaps to tangentspace
2872 ----------------------------------------------------------------- */
2875 if ( deluxemap && deluxemode == 1 ) {
2876 vec3_t worldUp, myNormal, myTangent, myBinormal;
2879 timer_start = I_FloatTime();
2881 Sys_Printf( "converting..." );
2883 for ( i = 0; i < numRawLightmaps; i++ )
2886 lm = &rawLightmaps[ i ];
2888 /* walk lightmap samples */
2889 for ( y = 0; y < lm->sh; y++ )
2891 for ( x = 0; x < lm->sw; x++ )
2893 /* get normal and deluxel */
2894 normal = SUPER_NORMAL( x, y );
2895 cluster = SUPER_CLUSTER( x, y );
2896 bspDeluxel = BSP_DELUXEL( x, y );
2897 deluxel = SUPER_DELUXEL( x, y );
2900 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2902 /* get tangent vectors */
2903 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2904 if ( myNormal[ 2 ] == 1.0f ) {
2905 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2906 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2908 else if ( myNormal[ 2 ] == -1.0f ) {
2909 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2910 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2915 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2916 CrossProduct( myNormal, worldUp, myTangent );
2917 VectorNormalize( myTangent, myTangent );
2918 CrossProduct( myTangent, myNormal, myBinormal );
2919 VectorNormalize( myBinormal, myBinormal );
2922 /* project onto plane */
2923 dist = -DotProduct( myTangent, myNormal );
2924 VectorMA( myTangent, dist, myNormal, myTangent );
2925 dist = -DotProduct( myBinormal, myNormal );
2926 VectorMA( myBinormal, dist, myNormal, myBinormal );
2929 VectorNormalize( myTangent, myTangent );
2930 VectorNormalize( myBinormal, myBinormal );
2932 /* convert modelspace deluxel to tangentspace */
2933 dirSample[0] = bspDeluxel[0];
2934 dirSample[1] = bspDeluxel[1];
2935 dirSample[2] = bspDeluxel[2];
2936 VectorNormalize( dirSample, dirSample );
2938 /* fix tangents to world matrix */
2939 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2940 VectorNegate( myTangent, myTangent );
2943 /* build tangentspace vectors */
2944 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2945 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2946 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2951 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
2955 /* -----------------------------------------------------------------
2957 ----------------------------------------------------------------- */
2959 #ifdef sdfsdfwq312323
2961 Sys_Printf( "blending..." );
2963 for ( i = 0; i < numRawLightmaps; i++ )
2969 lm = &rawLightmaps[ i ];
2971 /* walk individual lightmaps */
2972 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2975 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2979 /* walk lightmap samples */
2980 for ( y = 0; y < lm->sh; y++ )
2982 for ( x = 0; x < lm->sw; x++ )
2985 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2988 VectorNormalize( bspLuxel, myColor );
2989 myBrightness = VectorLength( bspLuxel );
2990 myBrightness *= ( 1 / 127.0f );
2991 myBrightness = myBrightness * myBrightness;
2992 myBrightness *= 127.0f;
2993 VectorScale( myColor, myBrightness, bspLuxel );
3000 /* -----------------------------------------------------------------
3001 collapse non-unique lightmaps
3002 ----------------------------------------------------------------- */
3004 if ( storeForReal && noCollapse == qfalse && deluxemap == qfalse ) {
3006 Sys_FPrintf( SYS_VRB, "collapsing..." );
3008 timer_start = I_FloatTime();
3010 /* set all twin refs to null */
3011 for ( i = 0; i < numRawLightmaps; i++ )
3013 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3015 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
3016 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
3017 rawLightmaps[ i ].numStyledTwins = 0;
3021 /* walk the list of raw lightmaps */
3022 for ( i = 0; i < numRawLightmaps; i++ )
3025 lm = &rawLightmaps[ i ];
3027 /* walk lightmaps */
3028 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3031 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
3032 lm->twins[ lightmapNum ] != NULL ) {
3036 /* find all lightmaps that are virtually identical to this one */
3037 for ( j = i + 1; j < numRawLightmaps; j++ )
3040 lm2 = &rawLightmaps[ j ];
3042 /* walk lightmaps */
3043 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3046 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3047 lm2->twins[ lightmapNum2 ] != NULL ) {
3052 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3053 /* merge and set twin */
3054 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3055 lm2->twins[ lightmapNum2 ] = lm;
3056 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3058 numTwinLuxels += ( lm->w * lm->h );
3060 /* count styled twins */
3061 if ( lightmapNum > 0 ) {
3062 lm->numStyledTwins++;
3071 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3074 /* -----------------------------------------------------------------
3075 sort raw lightmaps by shader
3076 ----------------------------------------------------------------- */
3079 Sys_FPrintf( SYS_VRB, "sorting..." );
3081 timer_start = I_FloatTime();
3083 /* allocate a new sorted list */
3084 if ( sortLightmaps == NULL ) {
3085 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3088 /* fill it out and sort it */
3089 for ( i = 0; i < numRawLightmaps; i++ )
3090 sortLightmaps[ i ] = i;
3091 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3093 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3095 /* -----------------------------------------------------------------
3096 allocate output lightmaps
3097 ----------------------------------------------------------------- */
3099 if ( storeForReal ) {
3101 Sys_FPrintf( SYS_VRB, "allocating..." );
3103 timer_start = I_FloatTime();
3105 /* kill all existing output lightmaps */
3106 if ( outLightmaps != NULL ) {
3107 for ( i = 0; i < numOutLightmaps; i++ )
3109 free( outLightmaps[ i ].lightBits );
3110 free( outLightmaps[ i ].bspLightBytes );
3112 free( outLightmaps );
3113 outLightmaps = NULL;
3116 numLightmapShaders = 0;
3117 numOutLightmaps = 0;
3118 numBSPLightmaps = 0;
3119 numExtLightmaps = 0;
3121 /* find output lightmap */
3122 for ( i = 0; i < numRawLightmaps; i++ )
3124 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3125 FindOutLightmaps( lm, fastAllocate );
3128 /* set output numbers in twinned lightmaps */
3129 for ( i = 0; i < numRawLightmaps; i++ )
3132 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3134 /* walk lightmaps */
3135 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3138 lm2 = lm->twins[ lightmapNum ];
3139 if ( lm2 == NULL ) {
3142 lightmapNum2 = lm->twinNums[ lightmapNum ];
3144 /* find output lightmap from twin */
3145 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3146 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3147 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3152 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3154 /* -----------------------------------------------------------------
3155 store output lightmaps
3156 ----------------------------------------------------------------- */
3158 if ( storeForReal ) {
3160 Sys_FPrintf( SYS_VRB, "storing..." );
3162 timer_start = I_FloatTime();
3164 /* count the bsp lightmaps and allocate space */
3165 if ( bspLightBytes != NULL ) {
3166 free( bspLightBytes );
3168 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3169 numBSPLightBytes = 0;
3170 bspLightBytes = NULL;
3174 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3175 bspLightBytes = safe_malloc0( numBSPLightBytes );
3178 /* walk the list of output lightmaps */
3179 for ( i = 0; i < numOutLightmaps; i++ )
3181 /* get output lightmap */
3182 olm = &outLightmaps[ i ];
3184 /* fill output lightmap */
3185 if ( lightmapFill ) {
3186 FillOutLightmap( olm );
3189 /* is this a valid bsp lightmap? */
3190 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3191 /* copy lighting data */
3192 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3193 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3195 /* copy direction data */
3197 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3198 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3202 /* external lightmap? */
3203 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3204 /* make a directory for the lightmaps */
3207 /* set external lightmap number */
3208 olm->extLightmapNum = numExtLightmaps;
3210 /* write lightmap */
3211 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3212 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3213 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3216 /* write deluxemap */
3218 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3219 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3220 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3223 if ( debugDeluxemap ) {
3224 olm->extLightmapNum++;
3230 if ( numExtLightmaps > 0 ) {
3231 Sys_FPrintf( SYS_VRB, "\n" );
3234 /* delete unused external lightmaps */
3235 for ( i = numExtLightmaps; i; i++ )
3237 /* determine if file exists */
3238 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3239 if ( !FileExists( filename ) ) {
3248 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3250 /* -----------------------------------------------------------------
3251 project the lightmaps onto the bsp surfaces
3252 ----------------------------------------------------------------- */
3254 if ( storeForReal ) {
3256 Sys_FPrintf( SYS_VRB, "projecting..." );
3258 timer_start = I_FloatTime();
3260 /* walk the list of surfaces */
3261 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3263 /* get the surface and info */
3264 ds = &bspDrawSurfaces[ i ];
3265 info = &surfaceInfos[ i ];
3269 /* handle surfaces with identical parent */
3270 if ( info->parentSurfaceNum >= 0 ) {
3271 /* preserve original data and get parent */
3272 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3273 memcpy( &dsTemp, ds, sizeof( *ds ) );
3275 /* overwrite child with parent data */
3276 memcpy( ds, parent, sizeof( *ds ) );
3278 /* restore key parts */
3279 ds->fogNum = dsTemp.fogNum;
3280 ds->firstVert = dsTemp.firstVert;
3281 ds->firstIndex = dsTemp.firstIndex;
3282 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3284 /* set vertex data */
3285 dv = &bspDrawVerts[ ds->firstVert ];
3286 dvParent = &bspDrawVerts[ parent->firstVert ];
3287 for ( j = 0; j < ds->numVerts; j++ )
3289 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3290 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3297 /* handle vertex lit or approximated surfaces */
3298 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3299 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3301 ds->lightmapNum[ lightmapNum ] = -3;
3302 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3306 /* handle lightmapped surfaces */
3309 /* walk lightmaps */
3310 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3313 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3315 /* handle unused style */
3316 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3317 ds->lightmapNum[ lightmapNum ] = -3;
3321 /* get output lightmap */
3322 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3324 /* set bsp lightmap number */
3325 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3327 /* deluxemap debugging makes the deluxemap visible */
3328 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3329 ds->lightmapNum[ lightmapNum ]++;
3332 /* calc lightmap origin in texture space */
3333 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3334 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3336 /* calc lightmap st coords */
3337 dv = &bspDrawVerts[ ds->firstVert ];
3338 ydv = &yDrawVerts[ ds->firstVert ];
3339 for ( j = 0; j < ds->numVerts; j++ )
3341 if ( lm->solid[ lightmapNum ] ) {
3342 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3343 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3347 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3348 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3354 /* store vertex colors */
3355 dv = &bspDrawVerts[ ds->firstVert ];
3356 for ( j = 0; j < ds->numVerts; j++ )
3358 /* walk lightmaps */
3359 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3361 /* handle unused style */
3362 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3363 VectorClear( color );
3367 /* get vertex color */
3368 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3369 VectorCopy( luxel, color );
3371 /* set minimum light */
3372 if ( lightmapNum == 0 ) {
3373 for ( k = 0; k < 3; k++ )
3374 if ( color[ k ] < minVertexLight[ k ] ) {
3375 color[ k ] = minVertexLight[ k ];
3380 /* store to bytes */
3381 if ( !info->si->noVertexLight ) {
3382 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3387 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3388 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3390 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3394 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3395 dv = &bspDrawVerts[ ds->firstVert ];
3397 /* depthFunc equal? */
3398 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3405 /* generate stages for styled lightmaps */
3406 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3409 style = lm->styles[ lightmapNum ];
3410 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3414 /* get output lightmap */
3415 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3418 if ( !externalLightmapNames
3419 && lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3420 strcpy( lightmapName, "$lightmap" );
3423 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3426 /* get rgbgen string */
3427 if ( rgbGenValues[ style ] == NULL ) {
3428 sprintf( key, "_style%drgbgen", style );
3429 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3430 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3431 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3435 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3436 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3442 /* get alphagen string */
3443 if ( alphaGenValues[ style ] == NULL ) {
3444 sprintf( key, "_style%dalphagen", style );
3445 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3447 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3448 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3451 alphaGen[ 0 ] = '\0';
3454 /* calculate st offset */
3455 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3456 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3458 /* create additional stage */
3459 if ( lmx == 0.0f && lmy == 0.0f ) {
3460 sprintf( styleStage, "\t{\n"
3461 "\t\tmap %s\n" /* lightmap */
3462 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3463 "%s" /* depthFunc equal */
3466 "\t\ttcGen lightmap\n"
3469 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3475 sprintf( styleStage, "\t{\n"
3476 "\t\tmap %s\n" /* lightmap */
3477 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3478 "%s" /* depthFunc equal */
3481 "\t\ttcGen lightmap\n"
3482 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3485 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3493 strcat( styleStages, styleStage );
3496 /* create custom shader */
3497 if ( info->si->styleMarker == 2 ) {
3498 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3501 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3504 /* emit remap command */
3505 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3508 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3509 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3510 //% Sys_Printf( ")\n" );
3513 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3514 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3515 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3516 /* get output lightmap */
3517 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3519 /* do some name mangling */
3520 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3522 /* create custom shader */
3523 csi = CustomShader( info->si, "$lightmap", lightmapName );
3526 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3527 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3528 //% Sys_Printf( ")\n" );
3531 /* use the normal plain-jane shader */
3533 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3538 Sys_FPrintf( SYS_VRB, "%d.", (int) ( I_FloatTime() - timer_start ) );
3541 Sys_FPrintf( SYS_VRB, "done.\n" );
3543 /* calc num stored */
3544 numStored = numBSPLightBytes / 3;
3545 efficiency = ( numStored <= 0 )
3547 : (float) numUsed / (float) numStored;
3549 if ( storeForReal ) {
3551 Sys_Printf( "%9d luxels used\n", numUsed );
3552 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3553 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3554 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3555 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3556 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3557 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3558 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3559 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3561 /* write map shader file */
3562 WriteMapShaderFile();