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 ) );
2176 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2177 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2178 free( outLightmaps );
2182 /* initialize both out lightmaps */
2183 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2184 SetupOutLightmap( lm, &outLightmaps[ k ] );
2186 /* set out lightmap */
2187 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2188 olm = &outLightmaps[ i ];
2190 /* set stamp xy origin to the first surface lightmap */
2191 if ( lightmapNum > 0 ) {
2192 x = lm->lightmapX[ 0 ];
2193 y = lm->lightmapY[ 0 ];
2197 /* if this is a style-using lightmap, it must be exported */
2198 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2199 olm->extLightmapNum = 0;
2202 /* add the surface lightmap to the bsp lightmap */
2203 lm->outLightmapNums[ lightmapNum ] = i;
2204 lm->lightmapX[ lightmapNum ] = x;
2205 lm->lightmapY[ lightmapNum ] = y;
2206 olm->numLightmaps++;
2209 for ( i = 0; i < lm->numLightSurfaces; i++ )
2211 /* get surface info */
2212 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2214 /* test for shader */
2215 for ( j = 0; j < olm->numShaders; j++ )
2217 if ( olm->shaders[ j ] == info->si ) {
2222 /* if it doesn't exist, add it */
2223 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2224 olm->shaders[ olm->numShaders ] = info->si;
2226 numLightmapShaders++;
2231 if ( lm->solid[ lightmapNum ] ) {
2241 /* mark the bits used */
2242 for ( y = 0; y < yMax; y++ )
2244 for ( x = 0; x < xMax; x++ )
2247 luxel = BSP_LUXEL( lightmapNum, x, y );
2248 deluxel = BSP_DELUXEL( x, y );
2249 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2253 /* set minimum light */
2254 if ( lm->solid[ lightmapNum ] ) {
2256 VectorSet( color, 255.0f, 0.0f, 0.0f );
2259 VectorCopy( lm->solidColor[ lightmapNum ], color );
2263 VectorCopy( luxel, color );
2266 /* styles are not affected by minlight */
2267 if ( lightmapNum == 0 ) {
2268 for ( i = 0; i < 3; i++ )
2270 if ( color[ i ] < minLight[ i ] ) {
2271 color[ i ] = minLight[ i ];
2276 /* get bsp lightmap coords */
2277 ox = x + lm->lightmapX[ lightmapNum ];
2278 oy = y + lm->lightmapY[ lightmapNum ];
2279 offset = ( oy * olm->customWidth ) + ox;
2281 /* flag pixel as used */
2282 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2286 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2287 ColorToBytes( color, pixel, lm->brightness );
2289 /* store direction */
2291 /* normalize average light direction */
2292 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2293 VectorScale( deluxel, 1000.0f, direction );
2294 VectorNormalize( direction, direction );
2295 VectorScale( direction, 127.5f, direction );
2296 for ( i = 0; i < 3; i++ )
2297 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2307 CompareRawLightmap()
2308 compare function for qsort()
2311 static int CompareRawLightmap( const void *a, const void *b ){
2312 rawLightmap_t *alm, *blm;
2313 surfaceInfo_t *aInfo, *bInfo;
2318 alm = &rawLightmaps[ *( (const int*) a ) ];
2319 blm = &rawLightmaps[ *( (const int*) b ) ];
2321 /* get min number of surfaces */
2322 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2325 for ( i = 0; i < min; i++ )
2327 /* get surface info */
2328 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2329 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2331 /* compare shader names */
2332 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2338 /* test style count */
2340 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2341 diff += blm->styles[ i ] - alm->styles[ i ];
2347 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2352 /* must be equivalent */
2358 void FillOutLightmap( outLightmap_t *olm ){
2361 vec3_t dir_sum, light_sum;
2363 byte *lightBitsNew = NULL;
2364 byte *lightBytesNew = NULL;
2365 byte *dirBytesNew = NULL;
2367 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2368 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2370 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2374 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2375 olm->lightBits[0] |= 1;
2376 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2377 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2378 olm->bspLightBytes[0] = 255;
2379 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2382 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2383 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2385 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2391 for ( y = 0; y < olm->customHeight; ++y )
2393 for ( x = 0; x < olm->customWidth; ++x )
2395 ofs = y * olm->customWidth + x;
2396 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2400 VectorClear( dir_sum );
2401 VectorClear( light_sum );
2403 /* try all four neighbors */
2404 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2405 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2407 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2409 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2413 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2414 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2416 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2418 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2422 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2423 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2425 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2427 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2431 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2432 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2434 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2436 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2442 ofs = y * olm->customWidth + x;
2443 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2444 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2446 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2456 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2457 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2459 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2463 free( lightBitsNew );
2464 free( lightBytesNew );
2466 free( dirBytesNew );
2473 StoreSurfaceLightmaps()
2474 stores the surface lightmaps into the bsp as byte rgb triplets
2477 void StoreSurfaceLightmaps( qboolean fastAllocate, qboolean storeForReal ){
2478 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2479 int style, size, lightmapNum, lightmapNum2;
2480 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2481 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2482 float *deluxel, *bspDeluxel, *bspDeluxel2;
2484 int numUsed, numTwins, numTwinLuxels, numStored;
2485 float lmx, lmy, efficiency;
2487 bspDrawSurface_t *ds, *parent, dsTemp;
2488 surfaceInfo_t *info;
2489 rawLightmap_t *lm, *lm2;
2491 bspDrawVert_t *dv, *ydv, *dvParent;
2492 char dirname[ 1024 ], filename[ 1024 ];
2494 char lightmapName[ 128 ];
2495 const char *rgbGenValues[ 256 ];
2496 const char *alphaGenValues[ 256 ];
2500 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2503 if ( lmCustomDir ) {
2504 strcpy( dirname, lmCustomDir );
2508 strcpy( dirname, source );
2509 StripExtension( dirname );
2511 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2512 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2514 /* -----------------------------------------------------------------
2515 average the sampled luxels into the bsp luxels
2516 ----------------------------------------------------------------- */
2519 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2521 /* walk the list of raw lightmaps */
2525 numSolidLightmaps = 0;
2526 for ( i = 0; i < numRawLightmaps; i++ )
2529 lm = &rawLightmaps[ i ];
2531 /* walk individual lightmaps */
2532 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2535 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2539 /* allocate bsp luxel storage */
2540 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2541 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2542 lm->bspLuxels[ lightmapNum ] = safe_malloc0( size );
2545 /* allocate radiosity lightmap storage */
2547 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2548 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2549 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2551 memset( lm->radLuxels[ lightmapNum ], 0, size );
2554 /* average supersampled luxels */
2555 for ( y = 0; y < lm->h; y++ )
2557 for ( x = 0; x < lm->w; x++ )
2561 occludedSamples = 0.0f;
2563 VectorClear( sample );
2564 VectorClear( occludedSample );
2565 VectorClear( dirSample );
2566 for ( ly = 0; ly < superSample; ly++ )
2568 for ( lx = 0; lx < superSample; lx++ )
2571 sx = x * superSample + lx;
2572 sy = y * superSample + ly;
2573 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2574 deluxel = SUPER_DELUXEL( sx, sy );
2575 normal = SUPER_NORMAL( sx, sy );
2576 cluster = SUPER_CLUSTER( sx, sy );
2578 /* sample deluxemap */
2579 if ( deluxemap && lightmapNum == 0 ) {
2580 VectorAdd( dirSample, deluxel, dirSample );
2583 /* keep track of used/occluded samples */
2584 if ( *cluster != CLUSTER_UNMAPPED ) {
2588 /* handle lightmap border? */
2589 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2590 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2595 else if ( debug && *cluster < 0 ) {
2596 if ( *cluster == CLUSTER_UNMAPPED ) {
2597 VectorSet( luxel, 255, 204, 0 );
2599 else if ( *cluster == CLUSTER_OCCLUDED ) {
2600 VectorSet( luxel, 255, 0, 255 );
2602 else if ( *cluster == CLUSTER_FLOODED ) {
2603 VectorSet( luxel, 0, 32, 255 );
2605 VectorAdd( occludedSample, luxel, occludedSample );
2606 occludedSamples += 1.0f;
2609 /* normal luxel handling */
2610 else if ( luxel[ 3 ] > 0.0f ) {
2611 /* handle lit or flooded luxels */
2612 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2613 VectorAdd( sample, luxel, sample );
2614 samples += luxel[ 3 ];
2617 /* handle occluded or unmapped luxels */
2620 VectorAdd( occludedSample, luxel, occludedSample );
2621 occludedSamples += luxel[ 3 ];
2624 /* handle style debugging */
2625 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2626 VectorCopy( debugColors[ 0 ], sample );
2633 /* only use occluded samples if necessary */
2634 if ( samples <= 0.0f ) {
2635 VectorCopy( occludedSample, sample );
2636 samples = occludedSamples;
2640 luxel = SUPER_LUXEL( lightmapNum, x, y );
2641 deluxel = SUPER_DELUXEL( x, y );
2643 /* store light direction */
2644 if ( deluxemap && lightmapNum == 0 ) {
2645 VectorCopy( dirSample, deluxel );
2648 /* store the sample back in super luxels */
2649 if ( samples > 0.01f ) {
2650 VectorScale( sample, ( 1.0f / samples ), luxel );
2654 /* if any samples were mapped in any way, store ambient color */
2655 else if ( mappedSamples > 0 ) {
2656 if ( lightmapNum == 0 ) {
2657 VectorCopy( ambientColor, luxel );
2660 VectorClear( luxel );
2665 /* store a bogus value to be fixed later */
2668 VectorClear( luxel );
2676 ClearBounds( colorMins, colorMaxs );
2678 /* clean up and store into bsp luxels */
2679 for ( y = 0; y < lm->h; y++ )
2681 for ( x = 0; x < lm->w; x++ )
2684 luxel = SUPER_LUXEL( lightmapNum, x, y );
2685 deluxel = SUPER_DELUXEL( x, y );
2687 /* copy light direction */
2688 if ( deluxemap && lightmapNum == 0 ) {
2689 VectorCopy( deluxel, dirSample );
2692 /* is this a valid sample? */
2693 if ( luxel[ 3 ] > 0.0f ) {
2694 VectorCopy( luxel, sample );
2695 samples = luxel[ 3 ];
2699 /* fix negative samples */
2700 for ( j = 0; j < 3; j++ )
2702 if ( sample[ j ] < 0.0f ) {
2709 /* nick an average value from the neighbors */
2710 VectorClear( sample );
2711 VectorClear( dirSample );
2714 /* fixme: why is this disabled?? */
2715 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2717 if ( sy < 0 || sy >= lm->h ) {
2721 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2723 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2727 /* get neighbor's particulars */
2728 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2729 if ( luxel[ 3 ] < 0.0f ) {
2732 VectorAdd( sample, luxel, sample );
2733 samples += luxel[ 3 ];
2738 if ( samples == 0.0f ) {
2739 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2747 /* fix negative samples */
2748 for ( j = 0; j < 3; j++ )
2750 if ( sample[ j ] < 0.0f ) {
2757 /* scale the sample */
2758 VectorScale( sample, ( 1.0f / samples ), sample );
2760 /* store the sample in the radiosity luxels */
2762 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2763 VectorCopy( sample, radLuxel );
2765 /* if only storing bounced light, early out here */
2766 if ( bounceOnly && !bouncing ) {
2771 /* store the sample in the bsp luxels */
2772 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2773 bspDeluxel = BSP_DELUXEL( x, y );
2775 VectorAdd( bspLuxel, sample, bspLuxel );
2776 if ( deluxemap && lightmapNum == 0 ) {
2777 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2780 /* add color to bounds for solid checking */
2781 if ( samples > 0.0f ) {
2782 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2787 /* set solid color */
2788 lm->solid[ lightmapNum ] = qfalse;
2789 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2790 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2792 /* nocollapse prevents solid lightmaps */
2793 if ( noCollapse == qfalse ) {
2794 /* check solid color */
2795 VectorSubtract( colorMaxs, colorMins, sample );
2796 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2797 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2799 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2800 lm->solid[ lightmapNum ] = qtrue;
2801 numSolidLightmaps++;
2804 /* if all lightmaps aren't solid, then none of them are solid */
2805 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2806 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2808 if ( lm->solid[ y ] ) {
2809 numSolidLightmaps--;
2811 lm->solid[ y ] = qfalse;
2816 /* wrap bsp luxels if necessary */
2817 if ( lm->wrap[ 0 ] ) {
2818 for ( y = 0; y < lm->h; y++ )
2820 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2821 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2822 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2823 VectorScale( bspLuxel, 0.5f, bspLuxel );
2824 VectorCopy( bspLuxel, bspLuxel2 );
2825 if ( deluxemap && lightmapNum == 0 ) {
2826 bspDeluxel = BSP_DELUXEL( 0, y );
2827 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2828 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2829 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2830 VectorCopy( bspDeluxel, bspDeluxel2 );
2834 if ( lm->wrap[ 1 ] ) {
2835 for ( x = 0; x < lm->w; x++ )
2837 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2838 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2839 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2840 VectorScale( bspLuxel, 0.5f, bspLuxel );
2841 VectorCopy( bspLuxel, bspLuxel2 );
2842 if ( deluxemap && lightmapNum == 0 ) {
2843 bspDeluxel = BSP_DELUXEL( x, 0 );
2844 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2845 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2846 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2847 VectorCopy( bspDeluxel, bspDeluxel2 );
2854 /* -----------------------------------------------------------------
2855 convert modelspace deluxemaps to tangentspace
2856 ----------------------------------------------------------------- */
2859 if ( deluxemap && deluxemode == 1 ) {
2860 vec3_t worldUp, myNormal, myTangent, myBinormal;
2863 Sys_Printf( "converting..." );
2865 for ( i = 0; i < numRawLightmaps; i++ )
2868 lm = &rawLightmaps[ i ];
2870 /* walk lightmap samples */
2871 for ( y = 0; y < lm->sh; y++ )
2873 for ( x = 0; x < lm->sw; x++ )
2875 /* get normal and deluxel */
2876 normal = SUPER_NORMAL( x, y );
2877 cluster = SUPER_CLUSTER( x, y );
2878 bspDeluxel = BSP_DELUXEL( x, y );
2879 deluxel = SUPER_DELUXEL( x, y );
2882 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2884 /* get tangent vectors */
2885 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2886 if ( myNormal[ 2 ] == 1.0f ) {
2887 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2888 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2890 else if ( myNormal[ 2 ] == -1.0f ) {
2891 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2892 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2897 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2898 CrossProduct( myNormal, worldUp, myTangent );
2899 VectorNormalize( myTangent, myTangent );
2900 CrossProduct( myTangent, myNormal, myBinormal );
2901 VectorNormalize( myBinormal, myBinormal );
2904 /* project onto plane */
2905 dist = -DotProduct( myTangent, myNormal );
2906 VectorMA( myTangent, dist, myNormal, myTangent );
2907 dist = -DotProduct( myBinormal, myNormal );
2908 VectorMA( myBinormal, dist, myNormal, myBinormal );
2911 VectorNormalize( myTangent, myTangent );
2912 VectorNormalize( myBinormal, myBinormal );
2914 /* convert modelspace deluxel to tangentspace */
2915 dirSample[0] = bspDeluxel[0];
2916 dirSample[1] = bspDeluxel[1];
2917 dirSample[2] = bspDeluxel[2];
2918 VectorNormalize( dirSample, dirSample );
2920 /* fix tangents to world matrix */
2921 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2922 VectorNegate( myTangent, myTangent );
2925 /* build tangentspace vectors */
2926 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2927 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2928 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2935 /* -----------------------------------------------------------------
2937 ----------------------------------------------------------------- */
2939 #ifdef sdfsdfwq312323
2941 Sys_Printf( "blending..." );
2943 for ( i = 0; i < numRawLightmaps; i++ )
2949 lm = &rawLightmaps[ i ];
2951 /* walk individual lightmaps */
2952 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2955 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2959 /* walk lightmap samples */
2960 for ( y = 0; y < lm->sh; y++ )
2962 for ( x = 0; x < lm->sw; x++ )
2965 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2968 VectorNormalize( bspLuxel, myColor );
2969 myBrightness = VectorLength( bspLuxel );
2970 myBrightness *= ( 1 / 127.0f );
2971 myBrightness = myBrightness * myBrightness;
2972 myBrightness *= 127.0f;
2973 VectorScale( myColor, myBrightness, bspLuxel );
2980 /* -----------------------------------------------------------------
2981 collapse non-unique lightmaps
2982 ----------------------------------------------------------------- */
2984 if ( storeForReal && noCollapse == qfalse && deluxemap == qfalse ) {
2986 Sys_FPrintf( SYS_VRB, "collapsing..." );
2988 /* set all twin refs to null */
2989 for ( i = 0; i < numRawLightmaps; i++ )
2991 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2993 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2994 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2995 rawLightmaps[ i ].numStyledTwins = 0;
2999 /* walk the list of raw lightmaps */
3000 for ( i = 0; i < numRawLightmaps; i++ )
3003 lm = &rawLightmaps[ i ];
3005 /* walk lightmaps */
3006 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3009 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
3010 lm->twins[ lightmapNum ] != NULL ) {
3014 /* find all lightmaps that are virtually identical to this one */
3015 for ( j = i + 1; j < numRawLightmaps; j++ )
3018 lm2 = &rawLightmaps[ j ];
3020 /* walk lightmaps */
3021 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3024 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3025 lm2->twins[ lightmapNum2 ] != NULL ) {
3030 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3031 /* merge and set twin */
3032 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3033 lm2->twins[ lightmapNum2 ] = lm;
3034 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3036 numTwinLuxels += ( lm->w * lm->h );
3038 /* count styled twins */
3039 if ( lightmapNum > 0 ) {
3040 lm->numStyledTwins++;
3050 /* -----------------------------------------------------------------
3051 sort raw lightmaps by shader
3052 ----------------------------------------------------------------- */
3055 Sys_FPrintf( SYS_VRB, "sorting..." );
3057 /* allocate a new sorted list */
3058 if ( sortLightmaps == NULL ) {
3059 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3062 /* fill it out and sort it */
3063 for ( i = 0; i < numRawLightmaps; i++ )
3064 sortLightmaps[ i ] = i;
3065 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3067 /* -----------------------------------------------------------------
3068 allocate output lightmaps
3069 ----------------------------------------------------------------- */
3071 if ( storeForReal ) {
3073 Sys_FPrintf( SYS_VRB, "allocating..." );
3075 /* kill all existing output lightmaps */
3076 if ( outLightmaps != NULL ) {
3077 for ( i = 0; i < numOutLightmaps; i++ )
3079 free( outLightmaps[ i ].lightBits );
3080 free( outLightmaps[ i ].bspLightBytes );
3082 free( outLightmaps );
3083 outLightmaps = NULL;
3086 numLightmapShaders = 0;
3087 numOutLightmaps = 0;
3088 numBSPLightmaps = 0;
3089 numExtLightmaps = 0;
3091 /* find output lightmap */
3092 for ( i = 0; i < numRawLightmaps; i++ )
3094 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3095 FindOutLightmaps( lm, fastAllocate );
3098 /* set output numbers in twinned lightmaps */
3099 for ( i = 0; i < numRawLightmaps; i++ )
3102 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3104 /* walk lightmaps */
3105 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3108 lm2 = lm->twins[ lightmapNum ];
3109 if ( lm2 == NULL ) {
3112 lightmapNum2 = lm->twinNums[ lightmapNum ];
3114 /* find output lightmap from twin */
3115 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3116 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3117 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3122 /* -----------------------------------------------------------------
3123 store output lightmaps
3124 ----------------------------------------------------------------- */
3126 if ( storeForReal ) {
3128 Sys_FPrintf( SYS_VRB, "storing..." );
3130 /* count the bsp lightmaps and allocate space */
3131 if ( bspLightBytes != NULL ) {
3132 free( bspLightBytes );
3134 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3135 numBSPLightBytes = 0;
3136 bspLightBytes = NULL;
3140 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3141 bspLightBytes = safe_malloc0( numBSPLightBytes );
3144 /* walk the list of output lightmaps */
3145 for ( i = 0; i < numOutLightmaps; i++ )
3147 /* get output lightmap */
3148 olm = &outLightmaps[ i ];
3150 /* fill output lightmap */
3151 if ( lightmapFill ) {
3152 FillOutLightmap( olm );
3155 /* is this a valid bsp lightmap? */
3156 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3157 /* copy lighting data */
3158 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3159 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3161 /* copy direction data */
3163 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3164 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3168 /* external lightmap? */
3169 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3170 /* make a directory for the lightmaps */
3173 /* set external lightmap number */
3174 olm->extLightmapNum = numExtLightmaps;
3176 /* write lightmap */
3177 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3178 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3179 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3182 /* write deluxemap */
3184 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3185 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3186 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3189 if ( debugDeluxemap ) {
3190 olm->extLightmapNum++;
3196 if ( numExtLightmaps > 0 ) {
3197 Sys_FPrintf( SYS_VRB, "\n" );
3200 /* delete unused external lightmaps */
3201 for ( i = numExtLightmaps; i; i++ )
3203 /* determine if file exists */
3204 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3205 if ( !FileExists( filename ) ) {
3214 /* -----------------------------------------------------------------
3215 project the lightmaps onto the bsp surfaces
3216 ----------------------------------------------------------------- */
3218 if ( storeForReal ) {
3220 Sys_FPrintf( SYS_VRB, "projecting..." );
3222 /* walk the list of surfaces */
3223 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3225 /* get the surface and info */
3226 ds = &bspDrawSurfaces[ i ];
3227 info = &surfaceInfos[ i ];
3231 /* handle surfaces with identical parent */
3232 if ( info->parentSurfaceNum >= 0 ) {
3233 /* preserve original data and get parent */
3234 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3235 memcpy( &dsTemp, ds, sizeof( *ds ) );
3237 /* overwrite child with parent data */
3238 memcpy( ds, parent, sizeof( *ds ) );
3240 /* restore key parts */
3241 ds->fogNum = dsTemp.fogNum;
3242 ds->firstVert = dsTemp.firstVert;
3243 ds->firstIndex = dsTemp.firstIndex;
3244 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3246 /* set vertex data */
3247 dv = &bspDrawVerts[ ds->firstVert ];
3248 dvParent = &bspDrawVerts[ parent->firstVert ];
3249 for ( j = 0; j < ds->numVerts; j++ )
3251 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3252 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3259 /* handle vertex lit or approximated surfaces */
3260 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3261 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3263 ds->lightmapNum[ lightmapNum ] = -3;
3264 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3268 /* handle lightmapped surfaces */
3271 /* walk lightmaps */
3272 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3275 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3277 /* handle unused style */
3278 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3279 ds->lightmapNum[ lightmapNum ] = -3;
3283 /* get output lightmap */
3284 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3286 /* set bsp lightmap number */
3287 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3289 /* deluxemap debugging makes the deluxemap visible */
3290 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3291 ds->lightmapNum[ lightmapNum ]++;
3294 /* calc lightmap origin in texture space */
3295 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3296 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3298 /* calc lightmap st coords */
3299 dv = &bspDrawVerts[ ds->firstVert ];
3300 ydv = &yDrawVerts[ ds->firstVert ];
3301 for ( j = 0; j < ds->numVerts; j++ )
3303 if ( lm->solid[ lightmapNum ] ) {
3304 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3305 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3309 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3310 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3316 /* store vertex colors */
3317 dv = &bspDrawVerts[ ds->firstVert ];
3318 for ( j = 0; j < ds->numVerts; j++ )
3320 /* walk lightmaps */
3321 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3323 /* handle unused style */
3324 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3325 VectorClear( color );
3329 /* get vertex color */
3330 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3331 VectorCopy( luxel, color );
3333 /* set minimum light */
3334 if ( lightmapNum == 0 ) {
3335 for ( k = 0; k < 3; k++ )
3336 if ( color[ k ] < minVertexLight[ k ] ) {
3337 color[ k ] = minVertexLight[ k ];
3342 /* store to bytes */
3343 if ( !info->si->noVertexLight ) {
3344 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3349 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3350 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3352 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3356 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3357 dv = &bspDrawVerts[ ds->firstVert ];
3359 /* depthFunc equal? */
3360 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3367 /* generate stages for styled lightmaps */
3368 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3371 style = lm->styles[ lightmapNum ];
3372 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3376 /* get output lightmap */
3377 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3380 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3381 strcpy( lightmapName, "$lightmap" );
3384 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3387 /* get rgbgen string */
3388 if ( rgbGenValues[ style ] == NULL ) {
3389 sprintf( key, "_style%drgbgen", style );
3390 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3391 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3392 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3396 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3397 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3403 /* get alphagen string */
3404 if ( alphaGenValues[ style ] == NULL ) {
3405 sprintf( key, "_style%dalphagen", style );
3406 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3408 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3409 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3412 alphaGen[ 0 ] = '\0';
3415 /* calculate st offset */
3416 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3417 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3419 /* create additional stage */
3420 if ( lmx == 0.0f && lmy == 0.0f ) {
3421 sprintf( styleStage, "\t{\n"
3422 "\t\tmap %s\n" /* lightmap */
3423 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3424 "%s" /* depthFunc equal */
3427 "\t\ttcGen lightmap\n"
3430 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3436 sprintf( styleStage, "\t{\n"
3437 "\t\tmap %s\n" /* lightmap */
3438 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3439 "%s" /* depthFunc equal */
3442 "\t\ttcGen lightmap\n"
3443 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3446 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3454 strcat( styleStages, styleStage );
3457 /* create custom shader */
3458 if ( info->si->styleMarker == 2 ) {
3459 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3462 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3465 /* emit remap command */
3466 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3469 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3470 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3471 //% Sys_Printf( ")\n" );
3474 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3475 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3476 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3477 /* get output lightmap */
3478 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3480 /* do some name mangling */
3481 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3483 /* create custom shader */
3484 csi = CustomShader( info->si, "$lightmap", lightmapName );
3487 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3488 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3489 //% Sys_Printf( ")\n" );
3492 /* use the normal plain-jane shader */
3494 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3500 Sys_FPrintf( SYS_VRB, "done.\n" );
3502 /* calc num stored */
3503 numStored = numBSPLightBytes / 3;
3504 efficiency = ( numStored <= 0 )
3506 : (float) numUsed / (float) numStored;
3508 if ( storeForReal ) {
3510 Sys_Printf( "%9d luxels used\n", numUsed );
3511 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3512 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3513 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3514 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3515 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3516 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3517 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3518 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3520 /* write map shader file */
3521 WriteMapShaderFile();