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 memset( buffer, 0, 18 );
72 buffer[ 12 ] = width & 255;
73 buffer[ 13 ] = width >> 8;
74 buffer[ 14 ] = height & 255;
75 buffer[ 15 ] = height >> 8;
79 c = ( width * height * 3 ) + 18;
80 for ( i = 18; i < c; i += 3 )
82 buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
83 buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
84 buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
87 /* write it and free the buffer */
88 file = fopen( filename, "wb" );
90 Error( "Unable to open %s for writing", filename );
93 /* flip vertically? */
95 fwrite( buffer, 1, 18, file );
96 for ( in = buffer + ( ( height - 1 ) * width * 3 ) + 18; in >= buffer; in -= ( width * 3 ) )
97 fwrite( in, 1, ( width * 3 ), file );
100 fwrite( buffer, 1, c, file );
112 exports the lightmaps as a list of numbered tga images
115 void ExportLightmaps( void ){
117 char dirname[ 1024 ], filename[ 1024 ];
122 Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n" );
124 /* do some path mangling */
125 strcpy( dirname, source );
126 StripExtension( dirname );
129 if ( bspLightBytes == NULL ) {
130 Sys_FPrintf( SYS_WRN, "WARNING: No BSP lightmap data\n" );
134 /* make a directory for the lightmaps */
137 /* iterate through the lightmaps */
138 for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
140 /* write a tga image out */
141 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
142 Sys_Printf( "Writing %s\n", filename );
143 WriteTGA24( filename, lightmap, game->lightmapSize, game->lightmapSize, qfalse );
150 ExportLightmapsMain()
151 exports the lightmaps as a list of numbered tga images
154 int ExportLightmapsMain( int argc, char **argv ){
157 Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
161 /* do some path mangling */
162 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
163 StripExtension( source );
164 DefaultExtension( source, ".bsp" );
167 Sys_Printf( "Loading %s\n", source );
168 LoadBSPFile( source );
170 /* export the lightmaps */
173 /* return to sender */
180 ImportLightmapsMain()
181 imports the lightmaps from a list of numbered tga images
184 int ImportLightmapsMain( int argc, char **argv ){
185 int i, x, y, len, width, height;
186 char dirname[ 1024 ], filename[ 1024 ];
187 byte *lightmap, *buffer, *pixels, *in, *out;
192 Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
196 /* do some path mangling */
197 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
198 StripExtension( source );
199 DefaultExtension( source, ".bsp" );
202 Sys_Printf( "Loading %s\n", source );
203 LoadBSPFile( source );
206 Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n" );
208 /* do some path mangling */
209 strcpy( dirname, source );
210 StripExtension( dirname );
213 if ( bspLightBytes == NULL ) {
214 Error( "No lightmap data" );
217 /* make a directory for the lightmaps */
220 /* iterate through the lightmaps */
221 for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
223 /* read a tga image */
224 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
225 Sys_Printf( "Loading %s\n", filename );
227 len = vfsLoadFile( filename, (void*) &buffer, -1 );
229 Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
233 /* parse file into an image */
235 LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
238 /* sanity check it */
239 if ( pixels == NULL ) {
240 Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
243 if ( width != game->lightmapSize || height != game->lightmapSize ) {
244 Sys_FPrintf( SYS_WRN, "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
245 filename, width, height, game->lightmapSize, game->lightmapSize );
248 /* copy the pixels */
250 for ( y = 1; y <= game->lightmapSize; y++ )
252 out = lightmap + ( ( game->lightmapSize - y ) * game->lightmapSize * 3 );
253 for ( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
254 VectorCopy( in, out );
262 Sys_Printf( "writing %s\n", source );
263 WriteBSPFile( source );
265 /* return to sender */
271 /* -------------------------------------------------------------------------------
273 this section deals with projecting a lightmap onto a raw drawsurface
275 ------------------------------------------------------------------------------- */
278 CompareLightSurface()
279 compare function for qsort()
282 static int CompareLightSurface( const void *a, const void *b ){
283 shaderInfo_t *asi, *bsi;
287 asi = surfaceInfos[ *( (const int*) a ) ].si;
288 bsi = surfaceInfos[ *( (const int*) b ) ].si;
298 /* compare shader names */
299 return strcmp( asi->shader, bsi->shader );
306 allocates a raw lightmap's necessary buffers
309 void FinishRawLightmap( rawLightmap_t *lm ){
310 int i, j, c, size, *sc;
315 /* sort light surfaces by shader name */
316 qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
319 lm->numLightClusters = 0;
320 for ( i = 0; i < lm->numLightSurfaces; i++ )
322 /* get surface info */
323 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
325 /* add surface clusters */
326 lm->numLightClusters += info->numSurfaceClusters;
329 /* allocate buffer for clusters and copy */
330 lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
332 for ( i = 0; i < lm->numLightSurfaces; i++ )
334 /* get surface info */
335 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
337 /* add surface clusters */
338 for ( j = 0; j < info->numSurfaceClusters; j++ )
339 lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
343 lm->styles[ 0 ] = LS_NORMAL;
344 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
345 lm->styles[ i ] = LS_NONE;
347 /* set supersampling size */
348 lm->sw = lm->w * superSample;
349 lm->sh = lm->h * superSample;
351 /* add to super luxel count */
352 numRawSuperLuxels += ( lm->sw * lm->sh );
354 /* manipulate origin/vecs for supersampling */
355 if ( superSample > 1 && lm->vecs != NULL ) {
356 /* calc inverse supersample */
357 is = 1.0f / superSample;
359 /* scale the vectors and shift the origin */
361 /* new code that works for arbitrary supersampling values */
362 VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
363 VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
364 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
365 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
366 VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
367 VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
369 /* old code that only worked with a value of 2 */
370 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
371 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
372 VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
373 VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
377 /* allocate bsp lightmap storage */
378 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
379 if ( lm->bspLuxels[ 0 ] == NULL ) {
380 lm->bspLuxels[ 0 ] = safe_malloc( size );
382 memset( lm->bspLuxels[ 0 ], 0, size );
384 /* allocate radiosity lightmap storage */
386 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
387 if ( lm->radLuxels[ 0 ] == NULL ) {
388 lm->radLuxels[ 0 ] = safe_malloc( size );
390 memset( lm->radLuxels[ 0 ], 0, size );
393 /* allocate sampling lightmap storage */
394 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
395 if ( lm->superLuxels[ 0 ] == NULL ) {
396 lm->superLuxels[ 0 ] = safe_malloc( size );
398 memset( lm->superLuxels[ 0 ], 0, size );
400 /* allocate origin map storage */
401 size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
402 if ( lm->superOrigins == NULL ) {
403 lm->superOrigins = safe_malloc( size );
405 memset( lm->superOrigins, 0, size );
407 /* allocate normal map storage */
408 size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
409 if ( lm->superNormals == NULL ) {
410 lm->superNormals = safe_malloc( size );
412 memset( lm->superNormals, 0, size );
414 /* allocate floodlight map storage */
415 size = lm->sw * lm->sh * SUPER_FLOODLIGHT_SIZE * sizeof( float );
416 if ( lm->superFloodLight == NULL ) {
417 lm->superFloodLight = safe_malloc( size );
419 memset( lm->superFloodLight, 0, size );
421 /* allocate cluster map storage */
422 size = lm->sw * lm->sh * sizeof( int );
423 if ( lm->superClusters == NULL ) {
424 lm->superClusters = safe_malloc( size );
426 size = lm->sw * lm->sh;
427 sc = lm->superClusters;
428 for ( i = 0; i < size; i++ )
429 ( *sc++ ) = CLUSTER_UNMAPPED;
431 /* deluxemap allocation */
433 /* allocate sampling deluxel storage */
434 size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
435 if ( lm->superDeluxels == NULL ) {
436 lm->superDeluxels = safe_malloc( size );
438 memset( lm->superDeluxels, 0, size );
440 /* allocate bsp deluxel storage */
441 size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
442 if ( lm->bspDeluxels == NULL ) {
443 lm->bspDeluxels = safe_malloc( size );
445 memset( lm->bspDeluxels, 0, size );
449 numLuxels += ( lm->sw * lm->sh );
455 AddPatchToRawLightmap()
456 projects a lightmap for a patch surface
457 since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
458 it is no longer necessary for patch verts to fall exactly on a lightmap sample
459 based on AllocateLightmapForPatch()
462 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm ){
463 bspDrawSurface_t *ds;
466 bspDrawVert_t *verts, *a, *b;
468 mesh_t src, *subdivided, *mesh;
469 float sBasis, tBasis, s, t;
470 float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
473 /* patches finish a raw lightmap */
474 lm->finished = qtrue;
476 /* get surface and info */
477 ds = &bspDrawSurfaces[ num ];
478 info = &surfaceInfos[ num ];
480 /* make a temporary mesh from the drawsurf */
481 src.width = ds->patchWidth;
482 src.height = ds->patchHeight;
483 src.verts = &yDrawVerts[ ds->firstVert ];
484 //% subdivided = SubdivideMesh( src, 8, 512 );
485 subdivided = SubdivideMesh2( src, info->patchIterations );
487 /* fit it to the curve and remove colinear verts on rows/columns */
488 PutMeshOnCurve( *subdivided );
489 mesh = RemoveLinearMeshColumnsRows( subdivided );
490 FreeMesh( subdivided );
492 /* find the longest distance on each row/column */
494 memset( widthTable, 0, sizeof( widthTable ) );
495 memset( heightTable, 0, sizeof( heightTable ) );
496 for ( y = 0; y < mesh->height; y++ )
498 for ( x = 0; x < mesh->width; x++ )
501 if ( x + 1 < mesh->width ) {
502 a = &verts[ ( y * mesh->width ) + x ];
503 b = &verts[ ( y * mesh->width ) + x + 1 ];
504 VectorSubtract( a->xyz, b->xyz, delta );
505 length = VectorLength( delta );
506 if ( length > widthTable[ x ] ) {
507 widthTable[ x ] = length;
512 if ( y + 1 < mesh->height ) {
513 a = &verts[ ( y * mesh->width ) + x ];
514 b = &verts[ ( ( y + 1 ) * mesh->width ) + x ];
515 VectorSubtract( a->xyz, b->xyz, delta );
516 length = VectorLength( delta );
517 if ( length > heightTable[ y ] ) {
518 heightTable[ y ] = length;
524 /* determine lightmap width */
526 for ( x = 0; x < ( mesh->width - 1 ); x++ )
527 length += widthTable[ x ];
528 lm->w = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
529 if ( lm->w < ds->patchWidth ) {
530 lm->w = ds->patchWidth;
532 if ( lm->w > lm->customWidth ) {
533 lm->w = lm->customWidth;
535 sBasis = (float) ( lm->w - 1 ) / (float) ( ds->patchWidth - 1 );
537 /* determine lightmap height */
539 for ( y = 0; y < ( mesh->height - 1 ); y++ )
540 length += heightTable[ y ];
541 lm->h = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
542 if ( lm->h < ds->patchHeight ) {
543 lm->h = ds->patchHeight;
545 if ( lm->h > lm->customHeight ) {
546 lm->h = lm->customHeight;
548 tBasis = (float) ( lm->h - 1 ) / (float) ( ds->patchHeight - 1 );
550 /* free the temporary mesh */
553 /* set the lightmap texture coordinates in yDrawVerts */
554 lm->wrap[ 0 ] = qtrue;
555 lm->wrap[ 1 ] = qtrue;
556 verts = &yDrawVerts[ ds->firstVert ];
557 for ( y = 0; y < ds->patchHeight; y++ )
559 t = ( tBasis * y ) + 0.5f;
560 for ( x = 0; x < ds->patchWidth; x++ )
562 s = ( sBasis * x ) + 0.5f;
563 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
564 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
566 if ( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ( ( ds->patchHeight - 1 ) * ds->patchWidth ) + x ].xyz ) ) {
567 lm->wrap[ 1 ] = qfalse;
571 if ( !VectorCompare( verts[ ( y * ds->patchWidth ) ].xyz, verts[ ( y * ds->patchWidth ) + ( ds->patchWidth - 1 ) ].xyz ) ) {
572 lm->wrap[ 0 ] = qfalse;
577 //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
578 //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
579 //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
580 //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
581 //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
584 numPatchesLightmapped++;
593 AddSurfaceToRawLightmap()
594 projects a lightmap for a surface
595 based on AllocateLightmapForSurface()
598 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm ){
599 bspDrawSurface_t *ds, *ds2;
601 int num2, n, i, axisNum;
602 float s, t, d, len, sampleSize;
603 vec3_t mins, maxs, origin, faxis, size, delta, normalized, vecs[ 2 ];
605 bspDrawVert_t *verts;
608 /* get surface and info */
609 ds = &bspDrawSurfaces[ num ];
610 info = &surfaceInfos[ num ];
612 /* add the surface to the raw lightmap */
613 lightSurfaces[ numLightSurfaces++ ] = num;
614 lm->numLightSurfaces++;
616 /* does this raw lightmap already have any surfaces? */
617 if ( lm->numLightSurfaces > 1 ) {
618 /* surface and raw lightmap must have the same lightmap projection axis */
619 if ( VectorCompare( info->axis, lm->axis ) == qfalse ) {
623 /* match identical attributes */
624 if ( info->sampleSize != lm->sampleSize ||
625 info->entityNum != lm->entityNum ||
626 info->recvShadows != lm->recvShadows ||
627 info->si->lmCustomWidth != lm->customWidth ||
628 info->si->lmCustomHeight != lm->customHeight ||
629 info->si->lmBrightness != lm->brightness ||
630 info->si->lmFilterRadius != lm->filterRadius ||
631 info->si->splotchFix != lm->splotchFix ) {
635 /* surface bounds must intersect with raw lightmap bounds */
636 for ( i = 0; i < 3; i++ )
638 if ( info->mins[ i ] > lm->maxs[ i ] ) {
641 if ( info->maxs[ i ] < lm->mins[ i ] ) {
646 /* plane check (fixme: allow merging of nonplanars) */
647 if ( info->si->lmMergable == qfalse ) {
648 if ( info->plane == NULL || lm->plane == NULL ) {
653 for ( i = 0; i < 4; i++ )
654 if ( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON ) {
659 /* debug code hacking */
660 //% if( lm->numLightSurfaces > 1 )
665 if ( info->plane == NULL ) {
669 /* add surface to lightmap bounds */
670 AddPointToBounds( info->mins, lm->mins, lm->maxs );
671 AddPointToBounds( info->maxs, lm->mins, lm->maxs );
673 /* check to see if this is a non-planar patch */
674 if ( ds->surfaceType == MST_PATCH &&
675 lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f ) {
676 return AddPatchToRawLightmap( num, lm );
679 /* start with initially requested sample size */
680 sampleSize = lm->sampleSize;
682 /* round to the lightmap resolution */
683 for ( i = 0; i < 3; i++ )
685 mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
686 maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
687 size[ i ] = ( maxs[ i ] - mins[ i ] ) / sampleSize + 1.0f;
689 /* hack (god this sucks) */
690 if ( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight || ( lmLimitSize && size[i] > lmLimitSize ) ) {
696 if ( sampleSize != lm->sampleSize && lmLimitSize == 0 ) {
697 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",
708 /* set actual sample size */
709 lm->actualSampleSize = sampleSize;
711 /* fixme: copy rounded mins/maxes to lightmap record? */
712 if ( lm->plane == NULL ) {
713 VectorCopy( mins, lm->mins );
714 VectorCopy( maxs, lm->maxs );
715 VectorCopy( mins, origin );
718 /* set lightmap origin */
719 VectorCopy( lm->mins, origin );
721 /* make absolute axis */
722 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
723 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
724 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
726 /* clear out lightmap vectors */
727 memset( vecs, 0, sizeof( vecs ) );
729 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
730 if ( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] ) {
734 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
735 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
737 else if ( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] ) {
741 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
742 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
749 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
750 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
753 /* check for bogus axis */
754 if ( faxis[ axisNum ] == 0.0f ) {
755 Sys_FPrintf( SYS_WRN, "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
760 /* store the axis number in the lightmap */
761 lm->axisNum = axisNum;
763 /* walk the list of surfaces on this raw lightmap */
764 for ( n = 0; n < lm->numLightSurfaces; n++ )
767 num2 = lightSurfaces[ lm->firstLightSurface + n ];
768 ds2 = &bspDrawSurfaces[ num2 ];
769 verts = &yDrawVerts[ ds2->firstVert ];
771 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
772 for ( i = 0; i < ds2->numVerts; i++ )
774 VectorSubtract( verts[ i ].xyz, origin, delta );
775 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
776 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
777 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
778 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
780 if ( s > (float) lm->w || t > (float) lm->h ) {
781 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
782 s, lm->w, t, lm->h );
787 /* get first drawsurface */
788 num2 = lightSurfaces[ lm->firstLightSurface ];
789 ds2 = &bspDrawSurfaces[ num2 ];
790 verts = &yDrawVerts[ ds2->firstVert ];
792 /* calculate lightmap origin */
793 if ( VectorLength( ds2->lightmapVecs[ 2 ] ) ) {
794 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
797 VectorCopy( lm->axis, plane );
799 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
801 VectorCopy( origin, lm->origin );
802 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
803 d /= plane[ axisNum ];
804 lm->origin[ axisNum ] -= d;
807 VectorCopy( lm->origin, ds->lightmapOrigin );
809 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
810 if ( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) { /* ydnar: can't remember what exactly i was thinking here... */
811 /* allocate space for the vectors */
812 lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
813 memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
814 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
816 /* project stepped lightmap blocks and subtract to get planevecs */
817 for ( i = 0; i < 2; i++ )
819 len = VectorNormalize( vecs[ i ], normalized );
820 VectorScale( normalized, ( 1.0 / len ), lm->vecs[ i ] );
821 d = DotProduct( lm->vecs[ i ], plane );
822 d /= plane[ axisNum ];
823 lm->vecs[ i ][ axisNum ] -= d;
828 /* lightmap vectors are useless on a non-planar surface */
833 if ( ds->surfaceType == MST_PATCH ) {
834 numPatchesLightmapped++;
835 if ( lm->plane != NULL ) {
836 numPlanarPatchesLightmapped++;
841 if ( lm->plane != NULL ) {
842 numPlanarsLightmapped++;
845 numNonPlanarsLightmapped++;
857 compare function for qsort()
860 static int CompareSurfaceInfo( const void *a, const void *b ){
861 surfaceInfo_t *aInfo, *bInfo;
865 /* get surface info */
866 aInfo = &surfaceInfos[ *( (const int*) a ) ];
867 bInfo = &surfaceInfos[ *( (const int*) b ) ];
870 if ( aInfo->modelindex < bInfo->modelindex ) {
873 else if ( aInfo->modelindex > bInfo->modelindex ) {
877 /* then lightmap status */
878 if ( aInfo->hasLightmap < bInfo->hasLightmap ) {
881 else if ( aInfo->hasLightmap > bInfo->hasLightmap ) {
885 /* 27: then shader! */
886 if ( aInfo->si < bInfo->si ) {
889 else if ( aInfo->si > bInfo->si ) {
893 /* then lightmap sample size */
894 if ( aInfo->sampleSize < bInfo->sampleSize ) {
897 else if ( aInfo->sampleSize > bInfo->sampleSize ) {
901 /* then lightmap axis */
902 for ( i = 0; i < 3; i++ )
904 if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
907 else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
913 if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
916 else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
919 else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
920 for ( i = 0; i < 4; i++ )
922 if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
925 else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
931 /* then position in world */
932 for ( i = 0; i < 3; i++ )
934 if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
937 else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
942 /* these are functionally identical (this should almost never happen) */
949 SetupSurfaceLightmaps()
950 allocates lightmaps for every surface in the bsp that needs one
951 this depends on yDrawVerts being allocated
954 void SetupSurfaceLightmaps( void ){
955 int i, j, k, s,num, num2;
958 bspDrawSurface_t *ds;
959 surfaceInfo_t *info, *info2;
962 vec3_t mapSize, entityOrigin;
966 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
968 /* determine supersample amount */
969 if ( superSample < 1 ) {
972 else if ( superSample > 8 ) {
973 Sys_FPrintf( SYS_WRN, "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
977 /* clear map bounds */
978 ClearBounds( mapMins, mapMaxs );
980 /* allocate a list of surface clusters */
981 numSurfaceClusters = 0;
982 maxSurfaceClusters = numBSPLeafSurfaces;
983 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
984 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
986 /* allocate a list for per-surface info */
987 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
988 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
989 for ( i = 0; i < numBSPDrawSurfaces; i++ )
990 surfaceInfos[ i ].childSurfaceNum = -1;
992 /* allocate a list of surface indexes to be sorted */
993 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
994 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
996 /* walk each model in the bsp */
997 for ( i = 0; i < numBSPModels; i++ )
1000 model = &bspModels[ i ];
1002 /* walk the list of surfaces in this model and fill out the info structs */
1003 for ( j = 0; j < model->numBSPSurfaces; j++ )
1005 /* make surface index */
1006 num = model->firstBSPSurface + j;
1008 /* copy index to sort list */
1009 sortSurfaces[ num ] = num;
1011 /* get surface and info */
1012 ds = &bspDrawSurfaces[ num ];
1013 info = &surfaceInfos[ num ];
1015 /* set entity origin */
1016 if ( ds->numVerts > 0 ) {
1017 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
1020 VectorClear( entityOrigin );
1024 info->modelindex = i;
1027 info->firstSurfaceCluster = numSurfaceClusters;
1029 /* get extra data */
1030 info->si = GetSurfaceExtraShaderInfo( num );
1031 if ( info->si == NULL ) {
1032 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
1034 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1035 info->entityNum = GetSurfaceExtraEntityNum( num );
1036 info->castShadows = GetSurfaceExtraCastShadows( num );
1037 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1038 info->sampleSize = GetSurfaceExtraSampleSize( num );
1039 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1040 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1041 GetSurfaceExtraLightmapAxis( num, info->axis );
1044 if ( info->parentSurfaceNum >= 0 ) {
1045 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1048 /* determine surface bounds */
1049 ClearBounds( info->mins, info->maxs );
1050 for ( k = 0; k < ds->numVerts; k++ )
1052 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1053 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1056 /* find all the bsp clusters the surface falls into */
1057 for ( k = 0; k < numBSPLeafs; k++ )
1060 leaf = &bspLeafs[ k ];
1063 if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1064 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1065 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
1069 /* test leaf surfaces */
1070 for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1072 if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
1073 if ( numSurfaceClusters >= maxSurfaceClusters ) {
1074 Error( "maxSurfaceClusters exceeded" );
1076 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1077 numSurfaceClusters++;
1078 info->numSurfaceClusters++;
1083 /* determine if surface is planar */
1084 if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
1086 info->plane = safe_malloc( 4 * sizeof( float ) );
1087 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1088 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1091 /* determine if surface requires a lightmap */
1092 if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
1093 ds->surfaceType == MST_FOLIAGE ||
1094 ( info->si->compileFlags & C_VERTEXLIT ) ) {
1095 numSurfsVertexLit++;
1099 numSurfsLightmapped++;
1100 info->hasLightmap = qtrue;
1105 /* find longest map distance */
1106 VectorSubtract( mapMaxs, mapMins, mapSize );
1107 maxMapDistance = VectorLength( mapSize );
1109 /* sort the surfaces info list */
1110 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1112 /* allocate a list of surfaces that would go into raw lightmaps */
1113 numLightSurfaces = 0;
1114 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1115 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1117 /* allocate a list of raw lightmaps */
1118 numRawSuperLuxels = 0;
1119 numRawLightmaps = 0;
1120 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1121 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1123 /* walk the list of sorted surfaces */
1124 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1126 /* get info and attempt early out */
1127 num = sortSurfaces[ i ];
1128 ds = &bspDrawSurfaces[ num ];
1129 info = &surfaceInfos[ num ];
1130 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1134 /* allocate a new raw lightmap */
1135 lm = &rawLightmaps[ numRawLightmaps ];
1139 lm->splotchFix = info->si->splotchFix;
1140 lm->firstLightSurface = numLightSurfaces;
1141 lm->numLightSurfaces = 0;
1142 /* vortex: multiply lightmap sample size by -samplescale */
1143 if ( sampleScale > 0 ) {
1144 lm->sampleSize = info->sampleSize * sampleScale;
1147 lm->sampleSize = info->sampleSize;
1149 lm->actualSampleSize = lm->sampleSize;
1150 lm->entityNum = info->entityNum;
1151 lm->recvShadows = info->recvShadows;
1152 lm->brightness = info->si->lmBrightness;
1153 lm->filterRadius = info->si->lmFilterRadius;
1154 VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
1155 lm->floodlightDistance = info->si->floodlightDistance;
1156 lm->floodlightIntensity = info->si->floodlightIntensity;
1157 lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
1158 VectorCopy( info->axis, lm->axis );
1159 lm->plane = info->plane;
1160 VectorCopy( info->mins, lm->mins );
1161 VectorCopy( info->maxs, lm->maxs );
1163 lm->customWidth = info->si->lmCustomWidth;
1164 lm->customHeight = info->si->lmCustomHeight;
1166 /* add the surface to the raw lightmap */
1167 AddSurfaceToRawLightmap( num, lm );
1170 /* do an exhaustive merge */
1174 /* walk the list of surfaces again */
1176 for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1178 /* get info and attempt early out */
1179 num2 = sortSurfaces[ j ];
1180 info2 = &surfaceInfos[ num2 ];
1181 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1185 /* add the surface to the raw lightmap */
1186 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1193 lm->numLightSurfaces--;
1199 /* finish the lightmap and allocate the various buffers */
1200 FinishRawLightmap( lm );
1203 /* allocate vertex luxel storage */
1204 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1206 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1207 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1208 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1209 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1212 /* emit some stats */
1213 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1214 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1215 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1216 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1217 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1218 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1219 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1220 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1226 StitchSurfaceLightmaps()
1227 stitches lightmap edges
1228 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1231 #define MAX_STITCH_CANDIDATES 32
1232 #define MAX_STITCH_LUXELS 64
1234 void StitchSurfaceLightmaps( void ){
1235 int i, j, x, y, x2, y2, *cluster, *cluster2,
1236 numStitched, numCandidates, numLuxels, f, fOld, start;
1237 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1238 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1239 sampleSize, average[ 3 ], totalColor, ootc;
1242 /* disabled for now */
1246 Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1250 start = I_FloatTime();
1252 /* walk the list of raw lightmaps */
1254 for ( i = 0; i < numRawLightmaps; i++ )
1256 /* print pacifier */
1257 f = 10 * i / numRawLightmaps;
1260 Sys_Printf( "%i...", f );
1263 /* get lightmap a */
1264 a = &rawLightmaps[ i ];
1266 /* walk rest of lightmaps */
1268 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1270 /* get lightmap b */
1271 b = &rawLightmaps[ j ];
1273 /* test bounding box */
1274 if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1275 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1276 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1281 c[ numCandidates++ ] = b;
1285 for ( y = 0; y < a->sh; y++ )
1287 for ( x = 0; x < a->sw; x++ )
1289 /* ignore unmapped/unlit luxels */
1291 cluster = SUPER_CLUSTER( x, y );
1292 if ( *cluster == CLUSTER_UNMAPPED ) {
1295 luxel = SUPER_LUXEL( 0, x, y );
1296 if ( luxel[ 3 ] <= 0.0f ) {
1300 /* get particulars */
1301 origin = SUPER_ORIGIN( x, y );
1302 normal = SUPER_NORMAL( x, y );
1304 /* walk candidate list */
1305 for ( j = 0; j < numCandidates; j++ )
1311 /* set samplesize to the smaller of the pair */
1312 sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1314 /* test bounding box */
1315 if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1316 origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1317 origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1321 /* walk candidate luxels */
1322 VectorClear( average );
1325 for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1327 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1329 /* ignore same luxels */
1330 if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1334 /* ignore unmapped/unlit luxels */
1335 cluster2 = SUPER_CLUSTER( x2, y2 );
1336 if ( *cluster2 == CLUSTER_UNMAPPED ) {
1339 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1340 if ( luxel2[ 3 ] <= 0.0f ) {
1344 /* get particulars */
1345 origin2 = SUPER_ORIGIN( x2, y2 );
1346 normal2 = SUPER_NORMAL( x2, y2 );
1349 if ( DotProduct( normal, normal2 ) < 0.5f ) {
1354 if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1355 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1356 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1361 //% VectorSet( luxel2, 255, 0, 255 );
1362 VectorAdd( average, luxel2, average );
1363 totalColor += luxel2[ 3 ];
1368 if ( numLuxels == 0 ) {
1373 ootc = 1.0f / totalColor;
1374 VectorScale( average, ootc, luxel );
1382 /* emit statistics */
1383 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1384 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1391 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1394 #define SOLID_EPSILON 0.0625
1395 #define LUXEL_TOLERANCE 0.0025
1396 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1398 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1401 double delta, total, rd, gd, bd;
1402 float *aLuxel, *bLuxel;
1405 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1406 if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1407 ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1412 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1413 a->brightness != b->brightness ||
1414 a->solid[ aNum ] != b->solid[ bNum ] ||
1415 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1419 /* compare solid color lightmaps */
1420 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1422 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1423 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1424 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1427 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1435 /* compare nonsolid lightmaps */
1436 if ( a->w != b->w || a->h != b->h ) {
1440 /* compare luxels */
1443 for ( y = 0; y < a->h; y++ )
1445 for ( x = 0; x < a->w; x++ )
1447 /* increment total */
1451 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1452 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1454 /* ignore unused luxels */
1455 if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1460 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1461 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1462 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1464 /* 2003-09-27: compare individual luxels */
1465 if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1469 /* compare (fixme: take into account perceptual differences) */
1470 delta += rd * LUXEL_COLOR_FRAC;
1471 delta += gd * LUXEL_COLOR_FRAC;
1472 delta += bd * LUXEL_COLOR_FRAC;
1474 /* is the change too high? */
1475 if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1481 /* made it this far, they must be identical (or close enough) */
1489 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1492 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1495 float luxel[ 3 ], *aLuxel, *bLuxel;
1499 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1500 a->brightness != b->brightness ||
1501 a->solid[ aNum ] != b->solid[ bNum ] ||
1502 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1506 /* compare solid lightmaps */
1507 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1509 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1510 VectorScale( luxel, 0.5f, luxel );
1513 VectorCopy( luxel, a->solidColor[ aNum ] );
1514 VectorCopy( luxel, b->solidColor[ bNum ] );
1516 /* return to sender */
1520 /* compare nonsolid lightmaps */
1521 if ( a->w != b->w || a->h != b->h ) {
1526 for ( y = 0; y < a->h; y++ )
1528 for ( x = 0; x < a->w; x++ )
1531 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1532 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1534 /* handle occlusion mismatch */
1535 if ( aLuxel[ 0 ] < 0.0f ) {
1536 VectorCopy( bLuxel, aLuxel );
1538 else if ( bLuxel[ 0 ] < 0.0f ) {
1539 VectorCopy( aLuxel, bLuxel );
1544 VectorAdd( aLuxel, bLuxel, luxel );
1545 VectorScale( luxel, 0.5f, luxel );
1547 /* debugging code */
1548 //% luxel[ 2 ] += 64.0f;
1551 VectorCopy( luxel, aLuxel );
1552 VectorCopy( luxel, bLuxel );
1565 determines if a single luxel is can be approximated with the interpolated vertex rgba
1568 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1569 int i, x, y, d, lightmapNum;
1571 vec3_t color, vertexColor;
1572 byte cb[ 4 ], vcb[ 4 ];
1575 /* find luxel xy coords */
1576 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1577 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1581 else if ( x >= lm->w ) {
1587 else if ( y >= lm->h ) {
1592 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1595 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1600 luxel = BSP_LUXEL( lightmapNum, x, y );
1602 /* ignore occluded luxels */
1603 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1607 /* copy, set min color and compare */
1608 VectorCopy( luxel, color );
1609 VectorCopy( dv->color[ 0 ], vertexColor );
1611 /* styles are not affected by minlight */
1612 if ( lightmapNum == 0 ) {
1613 for ( i = 0; i < 3; i++ )
1616 if ( color[ i ] < minLight[ i ] ) {
1617 color[ i ] = minLight[ i ];
1619 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1620 vertexColor[ i ] = minLight[ i ];
1626 ColorToBytes( color, cb, 1.0f );
1627 ColorToBytes( vertexColor, vcb, 1.0f );
1630 for ( i = 0; i < 3; i++ )
1632 d = cb[ i ] - vcb[ i ];
1636 if ( d > approximateTolerance ) {
1642 /* close enough for the girls i date */
1649 ApproximateTriangle()
1650 determines if a single triangle can be approximated with vertex rgba
1653 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1654 bspDrawVert_t mid, *dv2[ 3 ];
1658 /* approximate the vertexes */
1659 if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1662 if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1665 if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1669 /* subdivide calc */
1672 float dx, dy, dist, maxDist;
1675 /* find the longest edge and split it */
1678 for ( i = 0; i < 3; i++ )
1680 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1681 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1682 dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1683 if ( dist > maxDist ) {
1689 /* try to early out */
1690 if ( i < 0 || maxDist < subdivideThreshold ) {
1695 /* split the longest edge and map it */
1696 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1697 if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1701 /* recurse to first triangle */
1702 VectorCopy( dv, dv2 );
1704 if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1708 /* recurse to second triangle */
1709 VectorCopy( dv, dv2 );
1710 dv2[ ( max + 1 ) % 3 ] = ∣
1711 return ApproximateTriangle_r( lm, dv2 );
1717 ApproximateLightmap()
1718 determines if a raw lightmap can be approximated sufficiently with vertex colors
1721 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1722 int n, num, i, x, y, pw[ 5 ], r;
1723 bspDrawSurface_t *ds;
1724 surfaceInfo_t *info;
1725 mesh_t src, *subdivided, *mesh;
1726 bspDrawVert_t *verts, *dv[ 3 ];
1727 qboolean approximated;
1730 /* approximating? */
1731 if ( approximateTolerance <= 0 ) {
1735 /* test for jmonroe */
1737 /* don't approx lightmaps with styled twins */
1738 if ( lm->numStyledTwins > 0 ) {
1742 /* don't approx lightmaps with styles */
1743 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1745 if ( lm->styles[ i ] != LS_NONE ) {
1751 /* assume reduced until shadow detail is found */
1752 approximated = qtrue;
1754 /* walk the list of surfaces on this raw lightmap */
1755 for ( n = 0; n < lm->numLightSurfaces; n++ )
1758 num = lightSurfaces[ lm->firstLightSurface + n ];
1759 ds = &bspDrawSurfaces[ num ];
1760 info = &surfaceInfos[ num ];
1762 /* assume not-reduced initially */
1763 info->approximated = qfalse;
1765 /* bail if lightmap doesn't match up */
1766 if ( info->lm != lm ) {
1770 /* bail if not vertex lit */
1771 if ( info->si->noVertexLight ) {
1775 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1776 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1777 ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1778 ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1779 info->approximated = qtrue;
1780 numSurfsVertexForced++;
1784 /* handle the triangles */
1785 switch ( ds->surfaceType )
1789 verts = yDrawVerts + ds->firstVert;
1791 /* map the triangles */
1792 info->approximated = qtrue;
1793 for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1795 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1796 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1797 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1798 info->approximated = ApproximateTriangle_r( lm, dv );
1803 /* make a mesh from the drawsurf */
1804 src.width = ds->patchWidth;
1805 src.height = ds->patchHeight;
1806 src.verts = &yDrawVerts[ ds->firstVert ];
1807 //% subdivided = SubdivideMesh( src, 8, 512 );
1808 subdivided = SubdivideMesh2( src, info->patchIterations );
1810 /* fit it to the curve and remove colinear verts on rows/columns */
1811 PutMeshOnCurve( *subdivided );
1812 mesh = RemoveLinearMeshColumnsRows( subdivided );
1813 FreeMesh( subdivided );
1816 verts = mesh->verts;
1818 /* map the mesh quads */
1819 info->approximated = qtrue;
1820 for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1822 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1825 pw[ 0 ] = x + ( y * mesh->width );
1826 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1827 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1828 pw[ 3 ] = x + 1 + ( y * mesh->width );
1829 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1834 /* get drawverts and map first triangle */
1835 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1836 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1837 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1838 info->approximated = ApproximateTriangle_r( lm, dv );
1840 /* get drawverts and map second triangle */
1841 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1842 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1843 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1844 if ( info->approximated ) {
1845 info->approximated = ApproximateTriangle_r( lm, dv );
1859 if ( info->approximated == qfalse ) {
1860 approximated = qfalse;
1863 numSurfsVertexApproximated++;
1868 return approximated;
1874 TestOutLightmapStamp()
1875 tests a stamp on a given lightmap for validity
1878 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1879 int sx, sy, ox, oy, offset;
1884 if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1888 /* solid lightmaps test a 1x1 stamp */
1889 if ( lm->solid[ lightmapNum ] ) {
1890 offset = ( y * olm->customWidth ) + x;
1891 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1897 /* test the stamp */
1898 for ( sy = 0; sy < lm->h; sy++ )
1900 for ( sx = 0; sx < lm->w; sx++ )
1903 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1904 if ( luxel[ 0 ] < 0.0f ) {
1908 /* get bsp lightmap coords and test */
1911 offset = ( oy * olm->customWidth ) + ox;
1912 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1918 /* stamp is empty */
1926 sets up an output lightmap
1929 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1931 if ( lm == NULL || olm == NULL ) {
1935 /* is this a "normal" bsp-stored lightmap? */
1936 if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1937 olm->lightmapNum = numBSPLightmaps;
1940 /* lightmaps are interleaved with light direction maps */
1946 olm->lightmapNum = -3;
1949 /* set external lightmap number */
1950 olm->extLightmapNum = -1;
1953 olm->numLightmaps = 0;
1954 olm->customWidth = lm->customWidth;
1955 olm->customHeight = lm->customHeight;
1956 olm->freeLuxels = olm->customWidth * olm->customHeight;
1957 olm->numShaders = 0;
1959 /* allocate buffers */
1960 olm->lightBits = safe_malloc( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1961 memset( olm->lightBits, 0, ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1962 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1963 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1965 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1966 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1974 for a given surface lightmap, find output lightmap pages and positions for it
1977 #define LIGHTMAP_RESERVE_COUNT 1
1978 static void FindOutLightmaps( rawLightmap_t *lm, qboolean fastLightmapSearch ){
1979 int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
1981 surfaceInfo_t *info;
1982 float *luxel, *deluxel;
1983 vec3_t color, direction;
1986 int xIncrement, yIncrement;
1988 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1989 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1990 lm->outLightmapNums[ lightmapNum ] = -3;
1992 /* can this lightmap be approximated with vertex color? */
1993 if ( ApproximateLightmap( lm ) ) {
1998 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2001 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
2005 /* don't store twinned lightmaps */
2006 if ( lm->twins[ lightmapNum ] != NULL ) {
2010 /* if this is a styled lightmap, try some normalized locations first */
2012 if ( lightmapNum > 0 && outLightmaps != NULL ) {
2014 for ( j = 0; j < 2; j++ )
2016 /* try identical position */
2017 for ( i = 0; i < numOutLightmaps; i++ )
2019 /* get the output lightmap */
2020 olm = &outLightmaps[ i ];
2022 /* simple early out test */
2023 if ( olm->freeLuxels < lm->used ) {
2027 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2028 if ( olm->customWidth != lm->customWidth ||
2029 olm->customHeight != lm->customHeight ) {
2035 x = lm->lightmapX[ 0 ];
2036 y = lm->lightmapY[ 0 ];
2037 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2043 for ( sy = -1; sy <= 1; sy++ )
2045 for ( sx = -1; sx <= 1; sx++ )
2047 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
2048 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
2049 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2073 /* try normal placement algorithm */
2074 if ( ok == qfalse ) {
2079 /* walk the list of lightmap pages */
2080 if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
2084 i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
2086 for ( ; i < numOutLightmaps; i++ )
2088 /* get the output lightmap */
2089 olm = &outLightmaps[ i ];
2091 /* simple early out test */
2092 if ( olm->freeLuxels < lm->used ) {
2096 /* if fast allocation, skip lightmap files that are more than 90% complete */
2097 if ( fastLightmapSearch == qtrue ) {
2098 if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
2103 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2104 if ( olm->customWidth != lm->customWidth ||
2105 olm->customHeight != lm->customHeight ) {
2110 if ( lm->solid[ lightmapNum ] ) {
2111 xMax = olm->customWidth;
2112 yMax = olm->customHeight;
2116 xMax = ( olm->customWidth - lm->w ) + 1;
2117 yMax = ( olm->customHeight - lm->h ) + 1;
2120 /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
2121 if ( fastLightmapSearch == qtrue ) {
2122 xIncrement = MAX(1, lm->w / 15);
2123 yIncrement = MAX(1, lm->h / 15);
2130 /* walk the origin around the lightmap */
2131 for ( y = 0; y < yMax; y += yIncrement )
2133 for ( x = 0; x < xMax; x += xIncrement )
2135 /* find a fine tract of lauhnd */
2136 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2159 if ( ok == qfalse ) {
2160 /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2161 numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2162 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2163 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2164 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2165 free( outLightmaps );
2169 /* initialize both out lightmaps */
2170 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2171 SetupOutLightmap( lm, &outLightmaps[ k ] );
2173 /* set out lightmap */
2174 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2175 olm = &outLightmaps[ i ];
2177 /* set stamp xy origin to the first surface lightmap */
2178 if ( lightmapNum > 0 ) {
2179 x = lm->lightmapX[ 0 ];
2180 y = lm->lightmapY[ 0 ];
2184 /* if this is a style-using lightmap, it must be exported */
2185 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2186 olm->extLightmapNum = 0;
2189 /* add the surface lightmap to the bsp lightmap */
2190 lm->outLightmapNums[ lightmapNum ] = i;
2191 lm->lightmapX[ lightmapNum ] = x;
2192 lm->lightmapY[ lightmapNum ] = y;
2193 olm->numLightmaps++;
2196 for ( i = 0; i < lm->numLightSurfaces; i++ )
2198 /* get surface info */
2199 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2201 /* test for shader */
2202 for ( j = 0; j < olm->numShaders; j++ )
2204 if ( olm->shaders[ j ] == info->si ) {
2209 /* if it doesn't exist, add it */
2210 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2211 olm->shaders[ olm->numShaders ] = info->si;
2213 numLightmapShaders++;
2218 if ( lm->solid[ lightmapNum ] ) {
2228 /* mark the bits used */
2229 for ( y = 0; y < yMax; y++ )
2231 for ( x = 0; x < xMax; x++ )
2234 luxel = BSP_LUXEL( lightmapNum, x, y );
2235 deluxel = BSP_DELUXEL( x, y );
2236 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2240 /* set minimum light */
2241 if ( lm->solid[ lightmapNum ] ) {
2243 VectorSet( color, 255.0f, 0.0f, 0.0f );
2246 VectorCopy( lm->solidColor[ lightmapNum ], color );
2250 VectorCopy( luxel, color );
2253 /* styles are not affected by minlight */
2254 if ( lightmapNum == 0 ) {
2255 for ( i = 0; i < 3; i++ )
2257 if ( color[ i ] < minLight[ i ] ) {
2258 color[ i ] = minLight[ i ];
2263 /* get bsp lightmap coords */
2264 ox = x + lm->lightmapX[ lightmapNum ];
2265 oy = y + lm->lightmapY[ lightmapNum ];
2266 offset = ( oy * olm->customWidth ) + ox;
2268 /* flag pixel as used */
2269 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2273 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2274 ColorToBytes( color, pixel, lm->brightness );
2276 /* store direction */
2278 /* normalize average light direction */
2279 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2280 VectorScale( deluxel, 1000.0f, direction );
2281 VectorNormalize( direction, direction );
2282 VectorScale( direction, 127.5f, direction );
2283 for ( i = 0; i < 3; i++ )
2284 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2294 CompareRawLightmap()
2295 compare function for qsort()
2298 static int CompareRawLightmap( const void *a, const void *b ){
2299 rawLightmap_t *alm, *blm;
2300 surfaceInfo_t *aInfo, *bInfo;
2305 alm = &rawLightmaps[ *( (const int*) a ) ];
2306 blm = &rawLightmaps[ *( (const int*) b ) ];
2308 /* get min number of surfaces */
2309 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2312 for ( i = 0; i < min; i++ )
2314 /* get surface info */
2315 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2316 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2318 /* compare shader names */
2319 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2325 /* test style count */
2327 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2328 diff += blm->styles[ i ] - alm->styles[ i ];
2334 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2339 /* must be equivalent */
2345 void FillOutLightmap( outLightmap_t *olm ){
2348 vec3_t dir_sum, light_sum;
2350 byte *lightBitsNew = NULL;
2351 byte *lightBytesNew = NULL;
2352 byte *dirBytesNew = NULL;
2354 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2355 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2357 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2361 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2362 olm->lightBits[0] |= 1;
2363 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2364 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2365 olm->bspLightBytes[0] = 255;
2366 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2369 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2370 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2372 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2378 for ( y = 0; y < olm->customHeight; ++y )
2380 for ( x = 0; x < olm->customWidth; ++x )
2382 ofs = y * olm->customWidth + x;
2383 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2387 VectorClear( dir_sum );
2388 VectorClear( light_sum );
2390 /* try all four neighbors */
2391 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2392 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2394 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2396 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2400 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2401 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2403 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2405 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2409 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2410 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2412 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2414 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2418 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
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 );
2429 ofs = y * olm->customWidth + x;
2430 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2431 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2433 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2443 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2444 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2446 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2450 free( lightBitsNew );
2451 free( lightBytesNew );
2453 free( dirBytesNew );
2460 StoreSurfaceLightmaps()
2461 stores the surface lightmaps into the bsp as byte rgb triplets
2464 void StoreSurfaceLightmaps( qboolean fastLightmapSearch, qboolean storeForReal ){
2465 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2466 int style, size, lightmapNum, lightmapNum2;
2467 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2468 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2469 float *deluxel, *bspDeluxel, *bspDeluxel2;
2471 int numUsed, numTwins, numTwinLuxels, numStored;
2472 float lmx, lmy, efficiency;
2474 bspDrawSurface_t *ds, *parent, dsTemp;
2475 surfaceInfo_t *info;
2476 rawLightmap_t *lm, *lm2;
2478 bspDrawVert_t *dv, *ydv, *dvParent;
2479 char dirname[ 1024 ], filename[ 1024 ];
2481 char lightmapName[ 128 ];
2482 const char *rgbGenValues[ 256 ];
2483 const char *alphaGenValues[ 256 ];
2487 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2490 if ( lmCustomDir ) {
2491 strcpy( dirname, lmCustomDir );
2495 strcpy( dirname, source );
2496 StripExtension( dirname );
2498 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2499 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2501 /* -----------------------------------------------------------------
2502 average the sampled luxels into the bsp luxels
2503 ----------------------------------------------------------------- */
2506 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2508 /* walk the list of raw lightmaps */
2512 numSolidLightmaps = 0;
2513 for ( i = 0; i < numRawLightmaps; i++ )
2516 lm = &rawLightmaps[ i ];
2518 /* walk individual lightmaps */
2519 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2522 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2526 /* allocate bsp luxel storage */
2527 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2528 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2529 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2530 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2533 /* allocate radiosity lightmap storage */
2535 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2536 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2537 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2539 memset( lm->radLuxels[ lightmapNum ], 0, size );
2542 /* average supersampled luxels */
2543 for ( y = 0; y < lm->h; y++ )
2545 for ( x = 0; x < lm->w; x++ )
2549 occludedSamples = 0.0f;
2551 VectorClear( sample );
2552 VectorClear( occludedSample );
2553 VectorClear( dirSample );
2554 for ( ly = 0; ly < superSample; ly++ )
2556 for ( lx = 0; lx < superSample; lx++ )
2559 sx = x * superSample + lx;
2560 sy = y * superSample + ly;
2561 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2562 deluxel = SUPER_DELUXEL( sx, sy );
2563 normal = SUPER_NORMAL( sx, sy );
2564 cluster = SUPER_CLUSTER( sx, sy );
2566 /* sample deluxemap */
2567 if ( deluxemap && lightmapNum == 0 ) {
2568 VectorAdd( dirSample, deluxel, dirSample );
2571 /* keep track of used/occluded samples */
2572 if ( *cluster != CLUSTER_UNMAPPED ) {
2576 /* handle lightmap border? */
2577 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2578 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2583 else if ( debug && *cluster < 0 ) {
2584 if ( *cluster == CLUSTER_UNMAPPED ) {
2585 VectorSet( luxel, 255, 204, 0 );
2587 else if ( *cluster == CLUSTER_OCCLUDED ) {
2588 VectorSet( luxel, 255, 0, 255 );
2590 else if ( *cluster == CLUSTER_FLOODED ) {
2591 VectorSet( luxel, 0, 32, 255 );
2593 VectorAdd( occludedSample, luxel, occludedSample );
2594 occludedSamples += 1.0f;
2597 /* normal luxel handling */
2598 else if ( luxel[ 3 ] > 0.0f ) {
2599 /* handle lit or flooded luxels */
2600 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2601 VectorAdd( sample, luxel, sample );
2602 samples += luxel[ 3 ];
2605 /* handle occluded or unmapped luxels */
2608 VectorAdd( occludedSample, luxel, occludedSample );
2609 occludedSamples += luxel[ 3 ];
2612 /* handle style debugging */
2613 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2614 VectorCopy( debugColors[ 0 ], sample );
2621 /* only use occluded samples if necessary */
2622 if ( samples <= 0.0f ) {
2623 VectorCopy( occludedSample, sample );
2624 samples = occludedSamples;
2628 luxel = SUPER_LUXEL( lightmapNum, x, y );
2629 deluxel = SUPER_DELUXEL( x, y );
2631 /* store light direction */
2632 if ( deluxemap && lightmapNum == 0 ) {
2633 VectorCopy( dirSample, deluxel );
2636 /* store the sample back in super luxels */
2637 if ( samples > 0.01f ) {
2638 VectorScale( sample, ( 1.0f / samples ), luxel );
2642 /* if any samples were mapped in any way, store ambient color */
2643 else if ( mappedSamples > 0 ) {
2644 if ( lightmapNum == 0 ) {
2645 VectorCopy( ambientColor, luxel );
2648 VectorClear( luxel );
2653 /* store a bogus value to be fixed later */
2656 VectorClear( luxel );
2664 ClearBounds( colorMins, colorMaxs );
2666 /* clean up and store into bsp luxels */
2667 for ( y = 0; y < lm->h; y++ )
2669 for ( x = 0; x < lm->w; x++ )
2672 luxel = SUPER_LUXEL( lightmapNum, x, y );
2673 deluxel = SUPER_DELUXEL( x, y );
2675 /* copy light direction */
2676 if ( deluxemap && lightmapNum == 0 ) {
2677 VectorCopy( deluxel, dirSample );
2680 /* is this a valid sample? */
2681 if ( luxel[ 3 ] > 0.0f ) {
2682 VectorCopy( luxel, sample );
2683 samples = luxel[ 3 ];
2687 /* fix negative samples */
2688 for ( j = 0; j < 3; j++ )
2690 if ( sample[ j ] < 0.0f ) {
2697 /* nick an average value from the neighbors */
2698 VectorClear( sample );
2699 VectorClear( dirSample );
2702 /* fixme: why is this disabled?? */
2703 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2705 if ( sy < 0 || sy >= lm->h ) {
2709 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2711 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2715 /* get neighbor's particulars */
2716 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2717 if ( luxel[ 3 ] < 0.0f ) {
2720 VectorAdd( sample, luxel, sample );
2721 samples += luxel[ 3 ];
2726 if ( samples == 0.0f ) {
2727 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2735 /* fix negative samples */
2736 for ( j = 0; j < 3; j++ )
2738 if ( sample[ j ] < 0.0f ) {
2745 /* scale the sample */
2746 VectorScale( sample, ( 1.0f / samples ), sample );
2748 /* store the sample in the radiosity luxels */
2750 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2751 VectorCopy( sample, radLuxel );
2753 /* if only storing bounced light, early out here */
2754 if ( bounceOnly && !bouncing ) {
2759 /* store the sample in the bsp luxels */
2760 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2761 bspDeluxel = BSP_DELUXEL( x, y );
2763 VectorAdd( bspLuxel, sample, bspLuxel );
2764 if ( deluxemap && lightmapNum == 0 ) {
2765 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2768 /* add color to bounds for solid checking */
2769 if ( samples > 0.0f ) {
2770 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2775 /* set solid color */
2776 lm->solid[ lightmapNum ] = qfalse;
2777 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2778 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2780 /* nocollapse prevents solid lightmaps */
2781 if ( noCollapse == qfalse ) {
2782 /* check solid color */
2783 VectorSubtract( colorMaxs, colorMins, sample );
2784 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2785 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2787 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2788 lm->solid[ lightmapNum ] = qtrue;
2789 numSolidLightmaps++;
2792 /* if all lightmaps aren't solid, then none of them are solid */
2793 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2794 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2796 if ( lm->solid[ y ] ) {
2797 numSolidLightmaps--;
2799 lm->solid[ y ] = qfalse;
2804 /* wrap bsp luxels if necessary */
2805 if ( lm->wrap[ 0 ] ) {
2806 for ( y = 0; y < lm->h; y++ )
2808 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2809 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2810 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2811 VectorScale( bspLuxel, 0.5f, bspLuxel );
2812 VectorCopy( bspLuxel, bspLuxel2 );
2813 if ( deluxemap && lightmapNum == 0 ) {
2814 bspDeluxel = BSP_DELUXEL( 0, y );
2815 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2816 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2817 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2818 VectorCopy( bspDeluxel, bspDeluxel2 );
2822 if ( lm->wrap[ 1 ] ) {
2823 for ( x = 0; x < lm->w; x++ )
2825 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2826 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2827 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2828 VectorScale( bspLuxel, 0.5f, bspLuxel );
2829 VectorCopy( bspLuxel, bspLuxel2 );
2830 if ( deluxemap && lightmapNum == 0 ) {
2831 bspDeluxel = BSP_DELUXEL( x, 0 );
2832 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2833 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2834 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2835 VectorCopy( bspDeluxel, bspDeluxel2 );
2842 /* -----------------------------------------------------------------
2843 convert modelspace deluxemaps to tangentspace
2844 ----------------------------------------------------------------- */
2847 if ( deluxemap && deluxemode == 1 ) {
2848 vec3_t worldUp, myNormal, myTangent, myBinormal;
2851 Sys_Printf( "converting..." );
2853 for ( i = 0; i < numRawLightmaps; i++ )
2856 lm = &rawLightmaps[ i ];
2858 /* walk lightmap samples */
2859 for ( y = 0; y < lm->sh; y++ )
2861 for ( x = 0; x < lm->sw; x++ )
2863 /* get normal and deluxel */
2864 normal = SUPER_NORMAL( x, y );
2865 cluster = SUPER_CLUSTER( x, y );
2866 bspDeluxel = BSP_DELUXEL( x, y );
2867 deluxel = SUPER_DELUXEL( x, y );
2870 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2872 /* get tangent vectors */
2873 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2874 if ( myNormal[ 2 ] == 1.0f ) {
2875 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2876 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2878 else if ( myNormal[ 2 ] == -1.0f ) {
2879 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2880 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2885 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2886 CrossProduct( myNormal, worldUp, myTangent );
2887 VectorNormalize( myTangent, myTangent );
2888 CrossProduct( myTangent, myNormal, myBinormal );
2889 VectorNormalize( myBinormal, myBinormal );
2892 /* project onto plane */
2893 dist = -DotProduct( myTangent, myNormal );
2894 VectorMA( myTangent, dist, myNormal, myTangent );
2895 dist = -DotProduct( myBinormal, myNormal );
2896 VectorMA( myBinormal, dist, myNormal, myBinormal );
2899 VectorNormalize( myTangent, myTangent );
2900 VectorNormalize( myBinormal, myBinormal );
2902 /* convert modelspace deluxel to tangentspace */
2903 dirSample[0] = bspDeluxel[0];
2904 dirSample[1] = bspDeluxel[1];
2905 dirSample[2] = bspDeluxel[2];
2906 VectorNormalize( dirSample, dirSample );
2908 /* fix tangents to world matrix */
2909 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2910 VectorNegate( myTangent, myTangent );
2913 /* build tangentspace vectors */
2914 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2915 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2916 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2923 /* -----------------------------------------------------------------
2925 ----------------------------------------------------------------- */
2927 #ifdef sdfsdfwq312323
2929 Sys_Printf( "blending..." );
2931 for ( i = 0; i < numRawLightmaps; i++ )
2937 lm = &rawLightmaps[ i ];
2939 /* walk individual lightmaps */
2940 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2943 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2947 /* walk lightmap samples */
2948 for ( y = 0; y < lm->sh; y++ )
2950 for ( x = 0; x < lm->sw; x++ )
2953 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2956 VectorNormalize( bspLuxel, myColor );
2957 myBrightness = VectorLength( bspLuxel );
2958 myBrightness *= ( 1 / 127.0f );
2959 myBrightness = myBrightness * myBrightness;
2960 myBrightness *= 127.0f;
2961 VectorScale( myColor, myBrightness, bspLuxel );
2968 /* -----------------------------------------------------------------
2969 collapse non-unique lightmaps
2970 ----------------------------------------------------------------- */
2972 if ( storeForReal && noCollapse == qfalse && deluxemap == qfalse ) {
2974 Sys_FPrintf( SYS_VRB, "collapsing..." );
2976 /* set all twin refs to null */
2977 for ( i = 0; i < numRawLightmaps; i++ )
2979 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2981 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2982 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2983 rawLightmaps[ i ].numStyledTwins = 0;
2987 /* walk the list of raw lightmaps */
2988 for ( i = 0; i < numRawLightmaps; i++ )
2991 lm = &rawLightmaps[ i ];
2993 /* walk lightmaps */
2994 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2997 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
2998 lm->twins[ lightmapNum ] != NULL ) {
3002 /* find all lightmaps that are virtually identical to this one */
3003 for ( j = i + 1; j < numRawLightmaps; j++ )
3006 lm2 = &rawLightmaps[ j ];
3008 /* walk lightmaps */
3009 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3012 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3013 lm2->twins[ lightmapNum2 ] != NULL ) {
3018 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3019 /* merge and set twin */
3020 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3021 lm2->twins[ lightmapNum2 ] = lm;
3022 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3024 numTwinLuxels += ( lm->w * lm->h );
3026 /* count styled twins */
3027 if ( lightmapNum > 0 ) {
3028 lm->numStyledTwins++;
3038 /* -----------------------------------------------------------------
3039 sort raw lightmaps by shader
3040 ----------------------------------------------------------------- */
3043 Sys_FPrintf( SYS_VRB, "sorting..." );
3045 /* allocate a new sorted list */
3046 if ( sortLightmaps == NULL ) {
3047 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3050 /* fill it out and sort it */
3051 for ( i = 0; i < numRawLightmaps; i++ )
3052 sortLightmaps[ i ] = i;
3053 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3055 /* -----------------------------------------------------------------
3056 allocate output lightmaps
3057 ----------------------------------------------------------------- */
3059 if ( storeForReal ) {
3061 Sys_FPrintf( SYS_VRB, "allocating..." );
3063 /* kill all existing output lightmaps */
3064 if ( outLightmaps != NULL ) {
3065 for ( i = 0; i < numOutLightmaps; i++ )
3067 free( outLightmaps[ i ].lightBits );
3068 free( outLightmaps[ i ].bspLightBytes );
3070 free( outLightmaps );
3071 outLightmaps = NULL;
3074 numLightmapShaders = 0;
3075 numOutLightmaps = 0;
3076 numBSPLightmaps = 0;
3077 numExtLightmaps = 0;
3079 /* find output lightmap */
3080 for ( i = 0; i < numRawLightmaps; i++ )
3082 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3083 FindOutLightmaps( lm, fastLightmapSearch );
3086 /* set output numbers in twinned lightmaps */
3087 for ( i = 0; i < numRawLightmaps; i++ )
3090 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3092 /* walk lightmaps */
3093 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3096 lm2 = lm->twins[ lightmapNum ];
3097 if ( lm2 == NULL ) {
3100 lightmapNum2 = lm->twinNums[ lightmapNum ];
3102 /* find output lightmap from twin */
3103 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3104 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3105 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3110 /* -----------------------------------------------------------------
3111 store output lightmaps
3112 ----------------------------------------------------------------- */
3114 if ( storeForReal ) {
3116 Sys_FPrintf( SYS_VRB, "storing..." );
3118 /* count the bsp lightmaps and allocate space */
3119 if ( bspLightBytes != NULL ) {
3120 free( bspLightBytes );
3122 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3123 numBSPLightBytes = 0;
3124 bspLightBytes = NULL;
3128 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3129 bspLightBytes = safe_malloc( numBSPLightBytes );
3130 memset( bspLightBytes, 0, numBSPLightBytes );
3133 /* walk the list of output lightmaps */
3134 for ( i = 0; i < numOutLightmaps; i++ )
3136 /* get output lightmap */
3137 olm = &outLightmaps[ i ];
3139 /* fill output lightmap */
3140 if ( lightmapFill ) {
3141 FillOutLightmap( olm );
3144 /* is this a valid bsp lightmap? */
3145 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3146 /* copy lighting data */
3147 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3148 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3150 /* copy direction data */
3152 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3153 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3157 /* external lightmap? */
3158 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3159 /* make a directory for the lightmaps */
3162 /* set external lightmap number */
3163 olm->extLightmapNum = numExtLightmaps;
3165 /* write lightmap */
3166 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3167 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3168 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3171 /* write deluxemap */
3173 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3174 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3175 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3178 if ( debugDeluxemap ) {
3179 olm->extLightmapNum++;
3185 if ( numExtLightmaps > 0 ) {
3186 Sys_FPrintf( SYS_VRB, "\n" );
3189 /* delete unused external lightmaps */
3190 for ( i = numExtLightmaps; i; i++ )
3192 /* determine if file exists */
3193 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3194 if ( !FileExists( filename ) ) {
3203 /* -----------------------------------------------------------------
3204 project the lightmaps onto the bsp surfaces
3205 ----------------------------------------------------------------- */
3207 if ( storeForReal ) {
3209 Sys_FPrintf( SYS_VRB, "projecting..." );
3211 /* walk the list of surfaces */
3212 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3214 /* get the surface and info */
3215 ds = &bspDrawSurfaces[ i ];
3216 info = &surfaceInfos[ i ];
3220 /* handle surfaces with identical parent */
3221 if ( info->parentSurfaceNum >= 0 ) {
3222 /* preserve original data and get parent */
3223 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3224 memcpy( &dsTemp, ds, sizeof( *ds ) );
3226 /* overwrite child with parent data */
3227 memcpy( ds, parent, sizeof( *ds ) );
3229 /* restore key parts */
3230 ds->fogNum = dsTemp.fogNum;
3231 ds->firstVert = dsTemp.firstVert;
3232 ds->firstIndex = dsTemp.firstIndex;
3233 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3235 /* set vertex data */
3236 dv = &bspDrawVerts[ ds->firstVert ];
3237 dvParent = &bspDrawVerts[ parent->firstVert ];
3238 for ( j = 0; j < ds->numVerts; j++ )
3240 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3241 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3248 /* handle vertex lit or approximated surfaces */
3249 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3250 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3252 ds->lightmapNum[ lightmapNum ] = -3;
3253 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3257 /* handle lightmapped surfaces */
3260 /* walk lightmaps */
3261 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3264 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3266 /* handle unused style */
3267 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3268 ds->lightmapNum[ lightmapNum ] = -3;
3272 /* get output lightmap */
3273 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3275 /* set bsp lightmap number */
3276 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3278 /* deluxemap debugging makes the deluxemap visible */
3279 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3280 ds->lightmapNum[ lightmapNum ]++;
3283 /* calc lightmap origin in texture space */
3284 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3285 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3287 /* calc lightmap st coords */
3288 dv = &bspDrawVerts[ ds->firstVert ];
3289 ydv = &yDrawVerts[ ds->firstVert ];
3290 for ( j = 0; j < ds->numVerts; j++ )
3292 if ( lm->solid[ lightmapNum ] ) {
3293 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3294 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3298 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3299 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3305 /* store vertex colors */
3306 dv = &bspDrawVerts[ ds->firstVert ];
3307 for ( j = 0; j < ds->numVerts; j++ )
3309 /* walk lightmaps */
3310 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3312 /* handle unused style */
3313 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3314 VectorClear( color );
3318 /* get vertex color */
3319 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3320 VectorCopy( luxel, color );
3322 /* set minimum light */
3323 if ( lightmapNum == 0 ) {
3324 for ( k = 0; k < 3; k++ )
3325 if ( color[ k ] < minVertexLight[ k ] ) {
3326 color[ k ] = minVertexLight[ k ];
3331 /* store to bytes */
3332 if ( !info->si->noVertexLight ) {
3333 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3338 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3339 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3341 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3345 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3346 dv = &bspDrawVerts[ ds->firstVert ];
3348 /* depthFunc equal? */
3349 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3356 /* generate stages for styled lightmaps */
3357 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3360 style = lm->styles[ lightmapNum ];
3361 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3365 /* get output lightmap */
3366 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3369 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3370 strcpy( lightmapName, "$lightmap" );
3373 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3376 /* get rgbgen string */
3377 if ( rgbGenValues[ style ] == NULL ) {
3378 sprintf( key, "_style%drgbgen", style );
3379 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3380 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3381 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3385 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3386 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3392 /* get alphagen string */
3393 if ( alphaGenValues[ style ] == NULL ) {
3394 sprintf( key, "_style%dalphagen", style );
3395 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3397 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3398 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3401 alphaGen[ 0 ] = '\0';
3404 /* calculate st offset */
3405 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3406 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3408 /* create additional stage */
3409 if ( lmx == 0.0f && lmy == 0.0f ) {
3410 sprintf( styleStage, "\t{\n"
3411 "\t\tmap %s\n" /* lightmap */
3412 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3413 "%s" /* depthFunc equal */
3416 "\t\ttcGen lightmap\n"
3419 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3425 sprintf( styleStage, "\t{\n"
3426 "\t\tmap %s\n" /* lightmap */
3427 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3428 "%s" /* depthFunc equal */
3431 "\t\ttcGen lightmap\n"
3432 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3435 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3443 strcat( styleStages, styleStage );
3446 /* create custom shader */
3447 if ( info->si->styleMarker == 2 ) {
3448 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3451 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3454 /* emit remap command */
3455 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3458 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3459 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3460 //% Sys_Printf( ")\n" );
3463 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3464 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3465 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3466 /* get output lightmap */
3467 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3469 /* do some name mangling */
3470 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3472 /* create custom shader */
3473 csi = CustomShader( info->si, "$lightmap", lightmapName );
3476 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3477 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3478 //% Sys_Printf( ")\n" );
3481 /* use the normal plain-jane shader */
3483 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3489 Sys_FPrintf( SYS_VRB, "done.\n" );
3491 /* calc num stored */
3492 numStored = numBSPLightBytes / 3;
3493 efficiency = ( numStored <= 0 )
3495 : (float) numUsed / (float) numStored;
3497 if ( storeForReal ) {
3499 Sys_Printf( "%9d luxels used\n", numUsed );
3500 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3501 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3502 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3503 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3504 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3505 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3506 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3507 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3509 /* write map shader file */
3510 WriteMapShaderFile();