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_Printf( "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_Printf( "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_Printf( "WARNING: Unable to load image %s\n", filename );
243 if ( width != game->lightmapSize || height != game->lightmapSize ) {
244 Sys_Printf( "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 = ceil( length / lm->sampleSize ) + 1;
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 = ceil( length / lm->sampleSize ) + 1;
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_Printf( "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 ) {
894 /* then lightmap sample size */
895 if ( aInfo->sampleSize < bInfo->sampleSize ) {
898 else if ( aInfo->sampleSize > bInfo->sampleSize ) {
902 /* then lightmap axis */
903 for ( i = 0; i < 3; i++ )
905 if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
908 else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
914 if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
917 else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
920 else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
921 for ( i = 0; i < 4; i++ )
923 if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
926 else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
932 /* then position in world */
933 for ( i = 0; i < 3; i++ )
935 if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
938 else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
943 /* these are functionally identical (this should almost never happen) */
950 SetupSurfaceLightmaps()
951 allocates lightmaps for every surface in the bsp that needs one
952 this depends on yDrawVerts being allocated
955 void SetupSurfaceLightmaps( void ){
956 int i, j, k, s,num, num2;
959 bspDrawSurface_t *ds;
960 surfaceInfo_t *info, *info2;
963 vec3_t mapSize, entityOrigin;
967 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
969 /* determine supersample amount */
970 if ( superSample < 1 ) {
973 else if ( superSample > 8 ) {
974 Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
978 /* clear map bounds */
979 ClearBounds( mapMins, mapMaxs );
981 /* allocate a list of surface clusters */
982 numSurfaceClusters = 0;
983 maxSurfaceClusters = numBSPLeafSurfaces;
984 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
985 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
987 /* allocate a list for per-surface info */
988 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
989 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
990 for ( i = 0; i < numBSPDrawSurfaces; i++ )
991 surfaceInfos[ i ].childSurfaceNum = -1;
993 /* allocate a list of surface indexes to be sorted */
994 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
995 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
997 /* walk each model in the bsp */
998 for ( i = 0; i < numBSPModels; i++ )
1001 model = &bspModels[ i ];
1003 /* walk the list of surfaces in this model and fill out the info structs */
1004 for ( j = 0; j < model->numBSPSurfaces; j++ )
1006 /* make surface index */
1007 num = model->firstBSPSurface + j;
1009 /* copy index to sort list */
1010 sortSurfaces[ num ] = num;
1012 /* get surface and info */
1013 ds = &bspDrawSurfaces[ num ];
1014 info = &surfaceInfos[ num ];
1016 /* set entity origin */
1017 if ( ds->numVerts > 0 ) {
1018 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
1021 VectorClear( entityOrigin );
1025 info->modelindex = i;
1028 info->firstSurfaceCluster = numSurfaceClusters;
1030 /* get extra data */
1031 info->si = GetSurfaceExtraShaderInfo( num );
1032 if ( info->si == NULL ) {
1033 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
1035 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1036 info->entityNum = GetSurfaceExtraEntityNum( num );
1037 info->castShadows = GetSurfaceExtraCastShadows( num );
1038 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1039 info->sampleSize = GetSurfaceExtraSampleSize( num );
1040 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1041 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1042 GetSurfaceExtraLightmapAxis( num, info->axis );
1045 if ( info->parentSurfaceNum >= 0 ) {
1046 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1049 /* determine surface bounds */
1050 ClearBounds( info->mins, info->maxs );
1051 for ( k = 0; k < ds->numVerts; k++ )
1053 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1054 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1057 /* find all the bsp clusters the surface falls into */
1058 for ( k = 0; k < numBSPLeafs; k++ )
1061 leaf = &bspLeafs[ k ];
1064 if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1065 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1066 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
1070 /* test leaf surfaces */
1071 for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1073 if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
1074 if ( numSurfaceClusters >= maxSurfaceClusters ) {
1075 Error( "maxSurfaceClusters exceeded" );
1077 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1078 numSurfaceClusters++;
1079 info->numSurfaceClusters++;
1084 /* determine if surface is planar */
1085 if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
1087 info->plane = safe_malloc( 4 * sizeof( float ) );
1088 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1089 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1092 /* determine if surface requires a lightmap */
1093 if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
1094 ds->surfaceType == MST_FOLIAGE ||
1095 ( info->si->compileFlags & C_VERTEXLIT ) ) {
1096 numSurfsVertexLit++;
1100 numSurfsLightmapped++;
1101 info->hasLightmap = qtrue;
1106 /* find longest map distance */
1107 VectorSubtract( mapMaxs, mapMins, mapSize );
1108 maxMapDistance = VectorLength( mapSize );
1110 /* sort the surfaces info list */
1111 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1113 /* allocate a list of surfaces that would go into raw lightmaps */
1114 numLightSurfaces = 0;
1115 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1116 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1118 /* allocate a list of raw lightmaps */
1119 numRawSuperLuxels = 0;
1120 numRawLightmaps = 0;
1121 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1122 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1124 /* walk the list of sorted surfaces */
1125 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1127 /* get info and attempt early out */
1128 num = sortSurfaces[ i ];
1129 ds = &bspDrawSurfaces[ num ];
1130 info = &surfaceInfos[ num ];
1131 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1135 /* allocate a new raw lightmap */
1136 lm = &rawLightmaps[ numRawLightmaps ];
1140 lm->splotchFix = info->si->splotchFix;
1141 lm->firstLightSurface = numLightSurfaces;
1142 lm->numLightSurfaces = 0;
1143 /* vortex: multiply lightmap sample size by -samplescale */
1144 if ( sampleScale > 0 ) {
1145 lm->sampleSize = info->sampleSize * sampleScale;
1148 lm->sampleSize = info->sampleSize;
1150 lm->actualSampleSize = lm->sampleSize;
1151 lm->entityNum = info->entityNum;
1152 lm->recvShadows = info->recvShadows;
1153 lm->brightness = info->si->lmBrightness;
1154 lm->filterRadius = info->si->lmFilterRadius;
1155 VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
1156 lm->floodlightDistance = info->si->floodlightDistance;
1157 lm->floodlightIntensity = info->si->floodlightIntensity;
1158 lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
1159 VectorCopy( info->axis, lm->axis );
1160 lm->plane = info->plane;
1161 VectorCopy( info->mins, lm->mins );
1162 VectorCopy( info->maxs, lm->maxs );
1164 lm->customWidth = info->si->lmCustomWidth;
1165 lm->customHeight = info->si->lmCustomHeight;
1167 /* add the surface to the raw lightmap */
1168 AddSurfaceToRawLightmap( num, lm );
1171 /* do an exhaustive merge */
1175 /* walk the list of surfaces again */
1177 for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1179 /* get info and attempt early out */
1180 num2 = sortSurfaces[ j ];
1181 info2 = &surfaceInfos[ num2 ];
1182 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1186 /* add the surface to the raw lightmap */
1187 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1194 lm->numLightSurfaces--;
1200 /* finish the lightmap and allocate the various buffers */
1201 FinishRawLightmap( lm );
1204 /* allocate vertex luxel storage */
1205 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1207 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1208 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1209 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1210 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1213 /* emit some stats */
1214 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1215 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1216 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1217 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1218 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1219 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1220 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1221 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1227 StitchSurfaceLightmaps()
1228 stitches lightmap edges
1229 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1232 #define MAX_STITCH_CANDIDATES 32
1233 #define MAX_STITCH_LUXELS 64
1235 void StitchSurfaceLightmaps( void ){
1236 int i, j, x, y, x2, y2, *cluster, *cluster2,
1237 numStitched, numCandidates, numLuxels, f, fOld, start;
1238 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1239 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1240 sampleSize, average[ 3 ], totalColor, ootc;
1243 /* disabled for now */
1247 Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1251 start = I_FloatTime();
1253 /* walk the list of raw lightmaps */
1255 for ( i = 0; i < numRawLightmaps; i++ )
1257 /* print pacifier */
1258 f = 10 * i / numRawLightmaps;
1261 Sys_Printf( "%i...", f );
1264 /* get lightmap a */
1265 a = &rawLightmaps[ i ];
1267 /* walk rest of lightmaps */
1269 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1271 /* get lightmap b */
1272 b = &rawLightmaps[ j ];
1274 /* test bounding box */
1275 if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1276 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1277 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1282 c[ numCandidates++ ] = b;
1286 for ( y = 0; y < a->sh; y++ )
1288 for ( x = 0; x < a->sw; x++ )
1290 /* ignore unmapped/unlit luxels */
1292 cluster = SUPER_CLUSTER( x, y );
1293 if ( *cluster == CLUSTER_UNMAPPED ) {
1296 luxel = SUPER_LUXEL( 0, x, y );
1297 if ( luxel[ 3 ] <= 0.0f ) {
1301 /* get particulars */
1302 origin = SUPER_ORIGIN( x, y );
1303 normal = SUPER_NORMAL( x, y );
1305 /* walk candidate list */
1306 for ( j = 0; j < numCandidates; j++ )
1312 /* set samplesize to the smaller of the pair */
1313 sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1315 /* test bounding box */
1316 if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1317 origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1318 origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1322 /* walk candidate luxels */
1323 VectorClear( average );
1326 for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1328 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1330 /* ignore same luxels */
1331 if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1335 /* ignore unmapped/unlit luxels */
1336 cluster2 = SUPER_CLUSTER( x2, y2 );
1337 if ( *cluster2 == CLUSTER_UNMAPPED ) {
1340 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1341 if ( luxel2[ 3 ] <= 0.0f ) {
1345 /* get particulars */
1346 origin2 = SUPER_ORIGIN( x2, y2 );
1347 normal2 = SUPER_NORMAL( x2, y2 );
1350 if ( DotProduct( normal, normal2 ) < 0.5f ) {
1355 if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1356 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1357 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1362 //% VectorSet( luxel2, 255, 0, 255 );
1363 VectorAdd( average, luxel2, average );
1364 totalColor += luxel2[ 3 ];
1369 if ( numLuxels == 0 ) {
1374 ootc = 1.0f / totalColor;
1375 VectorScale( average, ootc, luxel );
1383 /* emit statistics */
1384 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1385 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1392 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1395 #define SOLID_EPSILON 0.0625
1396 #define LUXEL_TOLERANCE 0.0025
1397 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1399 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1402 double delta, total, rd, gd, bd;
1403 float *aLuxel, *bLuxel;
1406 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1407 if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1408 ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1413 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1414 a->brightness != b->brightness ||
1415 a->solid[ aNum ] != b->solid[ bNum ] ||
1416 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1420 /* compare solid color lightmaps */
1421 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1423 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1424 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1425 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1428 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1436 /* compare nonsolid lightmaps */
1437 if ( a->w != b->w || a->h != b->h ) {
1441 /* compare luxels */
1444 for ( y = 0; y < a->h; y++ )
1446 for ( x = 0; x < a->w; x++ )
1448 /* increment total */
1452 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1453 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1455 /* ignore unused luxels */
1456 if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1461 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1462 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1463 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1465 /* 2003-09-27: compare individual luxels */
1466 if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1470 /* compare (fixme: take into account perceptual differences) */
1471 delta += rd * LUXEL_COLOR_FRAC;
1472 delta += gd * LUXEL_COLOR_FRAC;
1473 delta += bd * LUXEL_COLOR_FRAC;
1475 /* is the change too high? */
1476 if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1482 /* made it this far, they must be identical (or close enough) */
1490 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1493 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1496 float luxel[ 3 ], *aLuxel, *bLuxel;
1500 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1501 a->brightness != b->brightness ||
1502 a->solid[ aNum ] != b->solid[ bNum ] ||
1503 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1507 /* compare solid lightmaps */
1508 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1510 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1511 VectorScale( luxel, 0.5f, luxel );
1514 VectorCopy( luxel, a->solidColor[ aNum ] );
1515 VectorCopy( luxel, b->solidColor[ bNum ] );
1517 /* return to sender */
1521 /* compare nonsolid lightmaps */
1522 if ( a->w != b->w || a->h != b->h ) {
1527 for ( y = 0; y < a->h; y++ )
1529 for ( x = 0; x < a->w; x++ )
1532 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1533 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1535 /* handle occlusion mismatch */
1536 if ( aLuxel[ 0 ] < 0.0f ) {
1537 VectorCopy( bLuxel, aLuxel );
1539 else if ( bLuxel[ 0 ] < 0.0f ) {
1540 VectorCopy( aLuxel, bLuxel );
1545 VectorAdd( aLuxel, bLuxel, luxel );
1546 VectorScale( luxel, 0.5f, luxel );
1548 /* debugging code */
1549 //% luxel[ 2 ] += 64.0f;
1552 VectorCopy( luxel, aLuxel );
1553 VectorCopy( luxel, bLuxel );
1566 determines if a single luxel is can be approximated with the interpolated vertex rgba
1569 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1570 int i, x, y, d, lightmapNum;
1572 vec3_t color, vertexColor;
1573 byte cb[ 4 ], vcb[ 4 ];
1576 /* find luxel xy coords */
1577 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1578 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1582 else if ( x >= lm->w ) {
1588 else if ( y >= lm->h ) {
1593 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1596 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1601 luxel = BSP_LUXEL( lightmapNum, x, y );
1603 /* ignore occluded luxels */
1604 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1608 /* copy, set min color and compare */
1609 VectorCopy( luxel, color );
1610 VectorCopy( dv->color[ 0 ], vertexColor );
1612 /* styles are not affected by minlight */
1613 if ( lightmapNum == 0 ) {
1614 for ( i = 0; i < 3; i++ )
1617 if ( color[ i ] < minLight[ i ] ) {
1618 color[ i ] = minLight[ i ];
1620 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1621 vertexColor[ i ] = minLight[ i ];
1627 ColorToBytes( color, cb, 1.0f );
1628 ColorToBytes( vertexColor, vcb, 1.0f );
1631 for ( i = 0; i < 3; i++ )
1633 d = cb[ i ] - vcb[ i ];
1637 if ( d > approximateTolerance ) {
1643 /* close enough for the girls i date */
1650 ApproximateTriangle()
1651 determines if a single triangle can be approximated with vertex rgba
1654 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1655 bspDrawVert_t mid, *dv2[ 3 ];
1659 /* approximate the vertexes */
1660 if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1663 if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1666 if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1670 /* subdivide calc */
1673 float dx, dy, dist, maxDist;
1676 /* find the longest edge and split it */
1679 for ( i = 0; i < 3; i++ )
1681 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1682 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1683 dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1684 if ( dist > maxDist ) {
1690 /* try to early out */
1691 if ( i < 0 || maxDist < subdivideThreshold ) {
1696 /* split the longest edge and map it */
1697 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1698 if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1702 /* recurse to first triangle */
1703 VectorCopy( dv, dv2 );
1705 if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1709 /* recurse to second triangle */
1710 VectorCopy( dv, dv2 );
1711 dv2[ ( max + 1 ) % 3 ] = ∣
1712 return ApproximateTriangle_r( lm, dv2 );
1718 ApproximateLightmap()
1719 determines if a raw lightmap can be approximated sufficiently with vertex colors
1722 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1723 int n, num, i, x, y, pw[ 5 ], r;
1724 bspDrawSurface_t *ds;
1725 surfaceInfo_t *info;
1726 mesh_t src, *subdivided, *mesh;
1727 bspDrawVert_t *verts, *dv[ 3 ];
1728 qboolean approximated;
1731 /* approximating? */
1732 if ( approximateTolerance <= 0 ) {
1736 /* test for jmonroe */
1738 /* don't approx lightmaps with styled twins */
1739 if ( lm->numStyledTwins > 0 ) {
1743 /* don't approx lightmaps with styles */
1744 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1746 if ( lm->styles[ i ] != LS_NONE ) {
1752 /* assume reduced until shadow detail is found */
1753 approximated = qtrue;
1755 /* walk the list of surfaces on this raw lightmap */
1756 for ( n = 0; n < lm->numLightSurfaces; n++ )
1759 num = lightSurfaces[ lm->firstLightSurface + n ];
1760 ds = &bspDrawSurfaces[ num ];
1761 info = &surfaceInfos[ num ];
1763 /* assume not-reduced initially */
1764 info->approximated = qfalse;
1766 /* bail if lightmap doesn't match up */
1767 if ( info->lm != lm ) {
1771 /* bail if not vertex lit */
1772 if ( info->si->noVertexLight ) {
1776 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1777 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1778 ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1779 ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1780 info->approximated = qtrue;
1781 numSurfsVertexForced++;
1785 /* handle the triangles */
1786 switch ( ds->surfaceType )
1790 verts = yDrawVerts + ds->firstVert;
1792 /* map the triangles */
1793 info->approximated = qtrue;
1794 for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1796 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1797 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1798 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1799 info->approximated = ApproximateTriangle_r( lm, dv );
1804 /* make a mesh from the drawsurf */
1805 src.width = ds->patchWidth;
1806 src.height = ds->patchHeight;
1807 src.verts = &yDrawVerts[ ds->firstVert ];
1808 //% subdivided = SubdivideMesh( src, 8, 512 );
1809 subdivided = SubdivideMesh2( src, info->patchIterations );
1811 /* fit it to the curve and remove colinear verts on rows/columns */
1812 PutMeshOnCurve( *subdivided );
1813 mesh = RemoveLinearMeshColumnsRows( subdivided );
1814 FreeMesh( subdivided );
1817 verts = mesh->verts;
1819 /* map the mesh quads */
1820 info->approximated = qtrue;
1821 for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1823 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1826 pw[ 0 ] = x + ( y * mesh->width );
1827 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1828 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1829 pw[ 3 ] = x + 1 + ( y * mesh->width );
1830 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1835 /* get drawverts and map first triangle */
1836 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1837 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1838 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1839 info->approximated = ApproximateTriangle_r( lm, dv );
1841 /* get drawverts and map second triangle */
1842 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1843 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1844 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1845 if ( info->approximated ) {
1846 info->approximated = ApproximateTriangle_r( lm, dv );
1860 if ( info->approximated == qfalse ) {
1861 approximated = qfalse;
1864 numSurfsVertexApproximated++;
1869 return approximated;
1875 TestOutLightmapStamp()
1876 tests a stamp on a given lightmap for validity
1879 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1880 int sx, sy, ox, oy, offset;
1885 if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1889 /* solid lightmaps test a 1x1 stamp */
1890 if ( lm->solid[ lightmapNum ] ) {
1891 offset = ( y * olm->customWidth ) + x;
1892 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1898 /* test the stamp */
1899 for ( sy = 0; sy < lm->h; sy++ )
1901 for ( sx = 0; sx < lm->w; sx++ )
1904 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1905 if ( luxel[ 0 ] < 0.0f ) {
1909 /* get bsp lightmap coords and test */
1912 offset = ( oy * olm->customWidth ) + ox;
1913 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1919 /* stamp is empty */
1927 sets up an output lightmap
1930 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1932 if ( lm == NULL || olm == NULL ) {
1936 /* is this a "normal" bsp-stored lightmap? */
1937 if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1938 olm->lightmapNum = numBSPLightmaps;
1941 /* lightmaps are interleaved with light direction maps */
1947 olm->lightmapNum = -3;
1950 /* set external lightmap number */
1951 olm->extLightmapNum = -1;
1954 olm->numLightmaps = 0;
1955 olm->customWidth = lm->customWidth;
1956 olm->customHeight = lm->customHeight;
1957 olm->freeLuxels = olm->customWidth * olm->customHeight;
1958 olm->numShaders = 0;
1960 /* allocate buffers */
1961 olm->lightBits = safe_malloc( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1962 memset( olm->lightBits, 0, ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1963 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1964 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1966 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1967 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1975 for a given surface lightmap, find output lightmap pages and positions for it
1978 #define LIGHTMAP_RESERVE_COUNT 1
1979 static void FindOutLightmaps( rawLightmap_t *lm ){
1980 int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
1982 surfaceInfo_t *info;
1983 float *luxel, *deluxel;
1984 vec3_t color, direction;
1989 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1990 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1991 lm->outLightmapNums[ lightmapNum ] = -3;
1993 /* can this lightmap be approximated with vertex color? */
1994 if ( ApproximateLightmap( lm ) ) {
1999 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2002 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
2006 /* don't store twinned lightmaps */
2007 if ( lm->twins[ lightmapNum ] != NULL ) {
2011 /* if this is a styled lightmap, try some normalized locations first */
2013 if ( lightmapNum > 0 && outLightmaps != NULL ) {
2015 for ( j = 0; j < 2; j++ )
2017 /* try identical position */
2018 for ( i = 0; i < numOutLightmaps; i++ )
2020 /* get the output lightmap */
2021 olm = &outLightmaps[ i ];
2023 /* simple early out test */
2024 if ( olm->freeLuxels < lm->used ) {
2028 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2029 if ( olm->customWidth != lm->customWidth ||
2030 olm->customHeight != lm->customHeight ) {
2036 x = lm->lightmapX[ 0 ];
2037 y = lm->lightmapY[ 0 ];
2038 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2044 for ( sy = -1; sy <= 1; sy++ )
2046 for ( sx = -1; sx <= 1; sx++ )
2048 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
2049 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
2050 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2074 /* try normal placement algorithm */
2075 if ( ok == qfalse ) {
2080 /* walk the list of lightmap pages */
2081 if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
2085 i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
2087 for ( ; i < numOutLightmaps; i++ )
2089 /* get the output lightmap */
2090 olm = &outLightmaps[ i ];
2092 /* simple early out test */
2093 if ( olm->freeLuxels < lm->used ) {
2097 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2098 if ( olm->customWidth != lm->customWidth ||
2099 olm->customHeight != lm->customHeight ) {
2104 if ( lm->solid[ lightmapNum ] ) {
2105 xMax = olm->customWidth;
2106 yMax = olm->customHeight;
2110 xMax = ( olm->customWidth - lm->w ) + 1;
2111 yMax = ( olm->customHeight - lm->h ) + 1;
2114 /* walk the origin around the lightmap */
2115 for ( y = 0; y < yMax; y++ )
2117 for ( x = 0; x < xMax; x++ )
2119 /* find a fine tract of lauhnd */
2120 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2143 if ( ok == qfalse ) {
2144 /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2145 numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2146 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2147 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2148 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2149 free( outLightmaps );
2153 /* initialize both out lightmaps */
2154 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2155 SetupOutLightmap( lm, &outLightmaps[ k ] );
2157 /* set out lightmap */
2158 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2159 olm = &outLightmaps[ i ];
2161 /* set stamp xy origin to the first surface lightmap */
2162 if ( lightmapNum > 0 ) {
2163 x = lm->lightmapX[ 0 ];
2164 y = lm->lightmapY[ 0 ];
2168 /* if this is a style-using lightmap, it must be exported */
2169 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2170 olm->extLightmapNum = 0;
2173 /* add the surface lightmap to the bsp lightmap */
2174 lm->outLightmapNums[ lightmapNum ] = i;
2175 lm->lightmapX[ lightmapNum ] = x;
2176 lm->lightmapY[ lightmapNum ] = y;
2177 olm->numLightmaps++;
2180 for ( i = 0; i < lm->numLightSurfaces; i++ )
2182 /* get surface info */
2183 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2185 /* test for shader */
2186 for ( j = 0; j < olm->numShaders; j++ )
2188 if ( olm->shaders[ j ] == info->si ) {
2193 /* if it doesn't exist, add it */
2194 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2195 olm->shaders[ olm->numShaders ] = info->si;
2197 numLightmapShaders++;
2202 if ( lm->solid[ lightmapNum ] ) {
2212 /* mark the bits used */
2213 for ( y = 0; y < yMax; y++ )
2215 for ( x = 0; x < xMax; x++ )
2218 luxel = BSP_LUXEL( lightmapNum, x, y );
2219 deluxel = BSP_DELUXEL( x, y );
2220 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2224 /* set minimum light */
2225 if ( lm->solid[ lightmapNum ] ) {
2227 VectorSet( color, 255.0f, 0.0f, 0.0f );
2230 VectorCopy( lm->solidColor[ lightmapNum ], color );
2234 VectorCopy( luxel, color );
2237 /* styles are not affected by minlight */
2238 if ( lightmapNum == 0 ) {
2239 for ( i = 0; i < 3; i++ )
2241 if ( color[ i ] < minLight[ i ] ) {
2242 color[ i ] = minLight[ i ];
2247 /* get bsp lightmap coords */
2248 ox = x + lm->lightmapX[ lightmapNum ];
2249 oy = y + lm->lightmapY[ lightmapNum ];
2250 offset = ( oy * olm->customWidth ) + ox;
2252 /* flag pixel as used */
2253 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2257 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2258 ColorToBytes( color, pixel, lm->brightness );
2260 /* store direction */
2262 /* normalize average light direction */
2263 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2264 VectorScale( deluxel, 1000.0f, direction );
2265 VectorNormalize( direction, direction );
2266 VectorScale( direction, 127.5f, direction );
2267 for ( i = 0; i < 3; i++ )
2268 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2278 CompareRawLightmap()
2279 compare function for qsort()
2282 static int CompareRawLightmap( const void *a, const void *b ){
2283 rawLightmap_t *alm, *blm;
2284 surfaceInfo_t *aInfo, *bInfo;
2289 alm = &rawLightmaps[ *( (const int*) a ) ];
2290 blm = &rawLightmaps[ *( (const int*) b ) ];
2292 /* get min number of surfaces */
2293 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2296 for ( i = 0; i < min; i++ )
2298 /* get surface info */
2299 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2300 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2302 /* compare shader names */
2303 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2309 /* test style count */
2311 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2312 diff += blm->styles[ i ] - alm->styles[ i ];
2318 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2323 /* must be equivalent */
2327 void FillOutLightmap( outLightmap_t *olm ){
2330 vec3_t dir_sum, light_sum;
2332 byte *lightBitsNew = NULL;
2333 byte *lightBytesNew = NULL;
2334 byte *dirBytesNew = NULL;
2336 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2337 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2339 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2343 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2344 olm->lightBits[0] |= 1;
2345 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2346 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2347 olm->bspLightBytes[0] = 255;
2348 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2351 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2352 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2354 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2360 for ( y = 0; y < olm->customHeight; ++y )
2362 for ( x = 0; x < olm->customWidth; ++x )
2364 ofs = y * olm->customWidth + x;
2365 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2369 VectorClear( dir_sum );
2370 VectorClear( light_sum );
2372 /* try all four neighbors */
2373 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2374 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2376 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2378 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2382 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2383 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2385 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2387 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2391 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
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 * olm->customWidth + ( x + 1 ) % olm->customWidth;
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 );
2411 ofs = y * olm->customWidth + x;
2412 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2413 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2415 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2425 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2426 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2428 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2432 free( lightBitsNew );
2433 free( lightBytesNew );
2435 free( dirBytesNew );
2440 StoreSurfaceLightmaps()
2441 stores the surface lightmaps into the bsp as byte rgb triplets
2444 void StoreSurfaceLightmaps( void ){
2445 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2446 int style, size, lightmapNum, lightmapNum2;
2447 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2448 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2449 float *deluxel, *bspDeluxel, *bspDeluxel2;
2451 int numUsed, numTwins, numTwinLuxels, numStored;
2452 float lmx, lmy, efficiency;
2454 bspDrawSurface_t *ds, *parent, dsTemp;
2455 surfaceInfo_t *info;
2456 rawLightmap_t *lm, *lm2;
2458 bspDrawVert_t *dv, *ydv, *dvParent;
2459 char dirname[ 1024 ], filename[ 1024 ];
2461 char lightmapName[ 128 ];
2462 const char *rgbGenValues[ 256 ];
2463 const char *alphaGenValues[ 256 ];
2467 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2470 if ( lmCustomDir ) {
2471 strcpy( dirname, lmCustomDir );
2475 strcpy( dirname, source );
2476 StripExtension( dirname );
2478 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2479 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2481 /* -----------------------------------------------------------------
2482 average the sampled luxels into the bsp luxels
2483 ----------------------------------------------------------------- */
2486 Sys_Printf( "Subsampling..." );
2488 /* walk the list of raw lightmaps */
2492 numSolidLightmaps = 0;
2493 for ( i = 0; i < numRawLightmaps; i++ )
2496 lm = &rawLightmaps[ i ];
2498 /* walk individual lightmaps */
2499 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2502 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2506 /* allocate bsp luxel storage */
2507 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2508 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2509 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2510 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2513 /* allocate radiosity lightmap storage */
2515 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2516 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2517 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2519 memset( lm->radLuxels[ lightmapNum ], 0, size );
2522 /* average supersampled luxels */
2523 for ( y = 0; y < lm->h; y++ )
2525 for ( x = 0; x < lm->w; x++ )
2529 occludedSamples = 0.0f;
2531 VectorClear( sample );
2532 VectorClear( occludedSample );
2533 VectorClear( dirSample );
2534 for ( ly = 0; ly < superSample; ly++ )
2536 for ( lx = 0; lx < superSample; lx++ )
2539 sx = x * superSample + lx;
2540 sy = y * superSample + ly;
2541 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2542 deluxel = SUPER_DELUXEL( sx, sy );
2543 normal = SUPER_NORMAL( sx, sy );
2544 cluster = SUPER_CLUSTER( sx, sy );
2546 /* sample deluxemap */
2547 if ( deluxemap && lightmapNum == 0 ) {
2548 VectorAdd( dirSample, deluxel, dirSample );
2551 /* keep track of used/occluded samples */
2552 if ( *cluster != CLUSTER_UNMAPPED ) {
2556 /* handle lightmap border? */
2557 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2558 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2563 else if ( debug && *cluster < 0 ) {
2564 if ( *cluster == CLUSTER_UNMAPPED ) {
2565 VectorSet( luxel, 255, 204, 0 );
2567 else if ( *cluster == CLUSTER_OCCLUDED ) {
2568 VectorSet( luxel, 255, 0, 255 );
2570 else if ( *cluster == CLUSTER_FLOODED ) {
2571 VectorSet( luxel, 0, 32, 255 );
2573 VectorAdd( occludedSample, luxel, occludedSample );
2574 occludedSamples += 1.0f;
2577 /* normal luxel handling */
2578 else if ( luxel[ 3 ] > 0.0f ) {
2579 /* handle lit or flooded luxels */
2580 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2581 VectorAdd( sample, luxel, sample );
2582 samples += luxel[ 3 ];
2585 /* handle occluded or unmapped luxels */
2588 VectorAdd( occludedSample, luxel, occludedSample );
2589 occludedSamples += luxel[ 3 ];
2592 /* handle style debugging */
2593 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2594 VectorCopy( debugColors[ 0 ], sample );
2601 /* only use occluded samples if necessary */
2602 if ( samples <= 0.0f ) {
2603 VectorCopy( occludedSample, sample );
2604 samples = occludedSamples;
2608 luxel = SUPER_LUXEL( lightmapNum, x, y );
2609 deluxel = SUPER_DELUXEL( x, y );
2611 /* store light direction */
2612 if ( deluxemap && lightmapNum == 0 ) {
2613 VectorCopy( dirSample, deluxel );
2616 /* store the sample back in super luxels */
2617 if ( samples > 0.01f ) {
2618 VectorScale( sample, ( 1.0f / samples ), luxel );
2622 /* if any samples were mapped in any way, store ambient color */
2623 else if ( mappedSamples > 0 ) {
2624 if ( lightmapNum == 0 ) {
2625 VectorCopy( ambientColor, luxel );
2628 VectorClear( luxel );
2633 /* store a bogus value to be fixed later */
2636 VectorClear( luxel );
2644 ClearBounds( colorMins, colorMaxs );
2646 /* clean up and store into bsp luxels */
2647 for ( y = 0; y < lm->h; y++ )
2649 for ( x = 0; x < lm->w; x++ )
2652 luxel = SUPER_LUXEL( lightmapNum, x, y );
2653 deluxel = SUPER_DELUXEL( x, y );
2655 /* copy light direction */
2656 if ( deluxemap && lightmapNum == 0 ) {
2657 VectorCopy( deluxel, dirSample );
2660 /* is this a valid sample? */
2661 if ( luxel[ 3 ] > 0.0f ) {
2662 VectorCopy( luxel, sample );
2663 samples = luxel[ 3 ];
2667 /* fix negative samples */
2668 for ( j = 0; j < 3; j++ )
2670 if ( sample[ j ] < 0.0f ) {
2677 /* nick an average value from the neighbors */
2678 VectorClear( sample );
2679 VectorClear( dirSample );
2682 /* fixme: why is this disabled?? */
2683 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2685 if ( sy < 0 || sy >= lm->h ) {
2689 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2691 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2695 /* get neighbor's particulars */
2696 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2697 if ( luxel[ 3 ] < 0.0f ) {
2700 VectorAdd( sample, luxel, sample );
2701 samples += luxel[ 3 ];
2706 if ( samples == 0.0f ) {
2707 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2715 /* fix negative samples */
2716 for ( j = 0; j < 3; j++ )
2718 if ( sample[ j ] < 0.0f ) {
2725 /* scale the sample */
2726 VectorScale( sample, ( 1.0f / samples ), sample );
2728 /* store the sample in the radiosity luxels */
2730 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2731 VectorCopy( sample, radLuxel );
2733 /* if only storing bounced light, early out here */
2734 if ( bounceOnly && !bouncing ) {
2739 /* store the sample in the bsp luxels */
2740 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2741 bspDeluxel = BSP_DELUXEL( x, y );
2743 VectorAdd( bspLuxel, sample, bspLuxel );
2744 if ( deluxemap && lightmapNum == 0 ) {
2745 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2748 /* add color to bounds for solid checking */
2749 if ( samples > 0.0f ) {
2750 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2755 /* set solid color */
2756 lm->solid[ lightmapNum ] = qfalse;
2757 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2758 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2760 /* nocollapse prevents solid lightmaps */
2761 if ( noCollapse == qfalse ) {
2762 /* check solid color */
2763 VectorSubtract( colorMaxs, colorMins, sample );
2764 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2765 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2767 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2768 lm->solid[ lightmapNum ] = qtrue;
2769 numSolidLightmaps++;
2772 /* if all lightmaps aren't solid, then none of them are solid */
2773 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2774 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2776 if ( lm->solid[ y ] ) {
2777 numSolidLightmaps--;
2779 lm->solid[ y ] = qfalse;
2784 /* wrap bsp luxels if necessary */
2785 if ( lm->wrap[ 0 ] ) {
2786 for ( y = 0; y < lm->h; y++ )
2788 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2789 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2790 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2791 VectorScale( bspLuxel, 0.5f, bspLuxel );
2792 VectorCopy( bspLuxel, bspLuxel2 );
2793 if ( deluxemap && lightmapNum == 0 ) {
2794 bspDeluxel = BSP_DELUXEL( 0, y );
2795 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2796 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2797 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2798 VectorCopy( bspDeluxel, bspDeluxel2 );
2802 if ( lm->wrap[ 1 ] ) {
2803 for ( x = 0; x < lm->w; x++ )
2805 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2806 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2807 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2808 VectorScale( bspLuxel, 0.5f, bspLuxel );
2809 VectorCopy( bspLuxel, bspLuxel2 );
2810 if ( deluxemap && lightmapNum == 0 ) {
2811 bspDeluxel = BSP_DELUXEL( x, 0 );
2812 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2813 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2814 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2815 VectorCopy( bspDeluxel, bspDeluxel2 );
2822 /* -----------------------------------------------------------------
2823 convert modelspace deluxemaps to tangentspace
2824 ----------------------------------------------------------------- */
2827 if ( deluxemap && deluxemode == 1 ) {
2828 vec3_t worldUp, myNormal, myTangent, myBinormal;
2831 Sys_Printf( "converting..." );
2833 for ( i = 0; i < numRawLightmaps; i++ )
2836 lm = &rawLightmaps[ i ];
2838 /* walk lightmap samples */
2839 for ( y = 0; y < lm->sh; y++ )
2841 for ( x = 0; x < lm->sw; x++ )
2843 /* get normal and deluxel */
2844 normal = SUPER_NORMAL( x, y );
2845 cluster = SUPER_CLUSTER( x, y );
2846 bspDeluxel = BSP_DELUXEL( x, y );
2847 deluxel = SUPER_DELUXEL( x, y );
2850 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2852 /* get tangent vectors */
2853 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2854 if ( myNormal[ 2 ] == 1.0f ) {
2855 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2856 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2858 else if ( myNormal[ 2 ] == -1.0f ) {
2859 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2860 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2865 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2866 CrossProduct( myNormal, worldUp, myTangent );
2867 VectorNormalize( myTangent, myTangent );
2868 CrossProduct( myTangent, myNormal, myBinormal );
2869 VectorNormalize( myBinormal, myBinormal );
2872 /* project onto plane */
2873 dist = -DotProduct( myTangent, myNormal );
2874 VectorMA( myTangent, dist, myNormal, myTangent );
2875 dist = -DotProduct( myBinormal, myNormal );
2876 VectorMA( myBinormal, dist, myNormal, myBinormal );
2879 VectorNormalize( myTangent, myTangent );
2880 VectorNormalize( myBinormal, myBinormal );
2882 /* convert modelspace deluxel to tangentspace */
2883 dirSample[0] = bspDeluxel[0];
2884 dirSample[1] = bspDeluxel[1];
2885 dirSample[2] = bspDeluxel[2];
2886 VectorNormalize( dirSample, dirSample );
2888 /* fix tangents to world matrix */
2889 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2890 VectorNegate( myTangent, myTangent );
2893 /* build tangentspace vectors */
2894 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2895 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2896 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2903 /* -----------------------------------------------------------------
2905 ----------------------------------------------------------------- */
2907 #ifdef sdfsdfwq312323
2909 Sys_Printf( "blending..." );
2911 for ( i = 0; i < numRawLightmaps; i++ )
2917 lm = &rawLightmaps[ i ];
2919 /* walk individual lightmaps */
2920 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2923 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2927 /* walk lightmap samples */
2928 for ( y = 0; y < lm->sh; y++ )
2930 for ( x = 0; x < lm->sw; x++ )
2933 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2936 VectorNormalize( bspLuxel, myColor );
2937 myBrightness = VectorLength( bspLuxel );
2938 myBrightness *= ( 1 / 127.0f );
2939 myBrightness = myBrightness * myBrightness;
2940 myBrightness *= 127.0f;
2941 VectorScale( myColor, myBrightness, bspLuxel );
2948 /* -----------------------------------------------------------------
2949 collapse non-unique lightmaps
2950 ----------------------------------------------------------------- */
2952 if ( noCollapse == qfalse && deluxemap == qfalse ) {
2954 Sys_Printf( "collapsing..." );
2956 /* set all twin refs to null */
2957 for ( i = 0; i < numRawLightmaps; i++ )
2959 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2961 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2962 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2963 rawLightmaps[ i ].numStyledTwins = 0;
2967 /* walk the list of raw lightmaps */
2968 for ( i = 0; i < numRawLightmaps; i++ )
2971 lm = &rawLightmaps[ i ];
2973 /* walk lightmaps */
2974 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2977 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
2978 lm->twins[ lightmapNum ] != NULL ) {
2982 /* find all lightmaps that are virtually identical to this one */
2983 for ( j = i + 1; j < numRawLightmaps; j++ )
2986 lm2 = &rawLightmaps[ j ];
2988 /* walk lightmaps */
2989 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
2992 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
2993 lm2->twins[ lightmapNum2 ] != NULL ) {
2998 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
2999 /* merge and set twin */
3000 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3001 lm2->twins[ lightmapNum2 ] = lm;
3002 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3004 numTwinLuxels += ( lm->w * lm->h );
3006 /* count styled twins */
3007 if ( lightmapNum > 0 ) {
3008 lm->numStyledTwins++;
3018 /* -----------------------------------------------------------------
3019 sort raw lightmaps by shader
3020 ----------------------------------------------------------------- */
3023 Sys_Printf( "sorting..." );
3025 /* allocate a new sorted list */
3026 if ( sortLightmaps == NULL ) {
3027 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3030 /* fill it out and sort it */
3031 for ( i = 0; i < numRawLightmaps; i++ )
3032 sortLightmaps[ i ] = i;
3033 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3035 /* -----------------------------------------------------------------
3036 allocate output lightmaps
3037 ----------------------------------------------------------------- */
3040 Sys_Printf( "allocating..." );
3042 /* kill all existing output lightmaps */
3043 if ( outLightmaps != NULL ) {
3044 for ( i = 0; i < numOutLightmaps; i++ )
3046 free( outLightmaps[ i ].lightBits );
3047 free( outLightmaps[ i ].bspLightBytes );
3049 free( outLightmaps );
3050 outLightmaps = NULL;
3053 numLightmapShaders = 0;
3054 numOutLightmaps = 0;
3055 numBSPLightmaps = 0;
3056 numExtLightmaps = 0;
3058 /* find output lightmap */
3059 for ( i = 0; i < numRawLightmaps; i++ )
3061 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3062 FindOutLightmaps( lm );
3065 /* set output numbers in twinned lightmaps */
3066 for ( i = 0; i < numRawLightmaps; i++ )
3069 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3071 /* walk lightmaps */
3072 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3075 lm2 = lm->twins[ lightmapNum ];
3076 if ( lm2 == NULL ) {
3079 lightmapNum2 = lm->twinNums[ lightmapNum ];
3081 /* find output lightmap from twin */
3082 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3083 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3084 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3088 /* -----------------------------------------------------------------
3089 store output lightmaps
3090 ----------------------------------------------------------------- */
3093 Sys_Printf( "storing..." );
3095 /* count the bsp lightmaps and allocate space */
3096 if ( bspLightBytes != NULL ) {
3097 free( bspLightBytes );
3099 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3100 numBSPLightBytes = 0;
3101 bspLightBytes = NULL;
3105 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3106 bspLightBytes = safe_malloc( numBSPLightBytes );
3107 memset( bspLightBytes, 0, numBSPLightBytes );
3110 /* walk the list of output lightmaps */
3111 for ( i = 0; i < numOutLightmaps; i++ )
3113 /* get output lightmap */
3114 olm = &outLightmaps[ i ];
3116 /* fill output lightmap */
3117 if ( lightmapFill ) {
3118 FillOutLightmap( olm );
3121 /* is this a valid bsp lightmap? */
3122 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3123 /* copy lighting data */
3124 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3125 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3127 /* copy direction data */
3129 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3130 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3134 /* external lightmap? */
3135 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3136 /* make a directory for the lightmaps */
3139 /* set external lightmap number */
3140 olm->extLightmapNum = numExtLightmaps;
3142 /* write lightmap */
3143 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3144 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3145 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3148 /* write deluxemap */
3150 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3151 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3152 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3155 if ( debugDeluxemap ) {
3156 olm->extLightmapNum++;
3162 if ( numExtLightmaps > 0 ) {
3166 /* delete unused external lightmaps */
3167 for ( i = numExtLightmaps; i; i++ )
3169 /* determine if file exists */
3170 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3171 if ( !FileExists( filename ) ) {
3179 /* -----------------------------------------------------------------
3180 project the lightmaps onto the bsp surfaces
3181 ----------------------------------------------------------------- */
3184 Sys_Printf( "projecting..." );
3186 /* walk the list of surfaces */
3187 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3189 /* get the surface and info */
3190 ds = &bspDrawSurfaces[ i ];
3191 info = &surfaceInfos[ i ];
3195 /* handle surfaces with identical parent */
3196 if ( info->parentSurfaceNum >= 0 ) {
3197 /* preserve original data and get parent */
3198 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3199 memcpy( &dsTemp, ds, sizeof( *ds ) );
3201 /* overwrite child with parent data */
3202 memcpy( ds, parent, sizeof( *ds ) );
3204 /* restore key parts */
3205 ds->fogNum = dsTemp.fogNum;
3206 ds->firstVert = dsTemp.firstVert;
3207 ds->firstIndex = dsTemp.firstIndex;
3208 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3210 /* set vertex data */
3211 dv = &bspDrawVerts[ ds->firstVert ];
3212 dvParent = &bspDrawVerts[ parent->firstVert ];
3213 for ( j = 0; j < ds->numVerts; j++ )
3215 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3216 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3223 /* handle vertex lit or approximated surfaces */
3224 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3225 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3227 ds->lightmapNum[ lightmapNum ] = -3;
3228 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3232 /* handle lightmapped surfaces */
3235 /* walk lightmaps */
3236 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3239 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3241 /* handle unused style */
3242 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3243 ds->lightmapNum[ lightmapNum ] = -3;
3247 /* get output lightmap */
3248 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3250 /* set bsp lightmap number */
3251 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3253 /* deluxemap debugging makes the deluxemap visible */
3254 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3255 ds->lightmapNum[ lightmapNum ]++;
3258 /* calc lightmap origin in texture space */
3259 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3260 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3262 /* calc lightmap st coords */
3263 dv = &bspDrawVerts[ ds->firstVert ];
3264 ydv = &yDrawVerts[ ds->firstVert ];
3265 for ( j = 0; j < ds->numVerts; j++ )
3267 if ( lm->solid[ lightmapNum ] ) {
3268 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3269 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3273 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3274 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3280 /* store vertex colors */
3281 dv = &bspDrawVerts[ ds->firstVert ];
3282 for ( j = 0; j < ds->numVerts; j++ )
3284 /* walk lightmaps */
3285 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3287 /* handle unused style */
3288 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3289 VectorClear( color );
3293 /* get vertex color */
3294 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3295 VectorCopy( luxel, color );
3297 /* set minimum light */
3298 if ( lightmapNum == 0 ) {
3299 for ( k = 0; k < 3; k++ )
3300 if ( color[ k ] < minVertexLight[ k ] ) {
3301 color[ k ] = minVertexLight[ k ];
3306 /* store to bytes */
3307 if ( !info->si->noVertexLight ) {
3308 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3313 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3314 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3316 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3320 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3321 dv = &bspDrawVerts[ ds->firstVert ];
3323 /* depthFunc equal? */
3324 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3331 /* generate stages for styled lightmaps */
3332 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3335 style = lm->styles[ lightmapNum ];
3336 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3340 /* get output lightmap */
3341 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3344 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3345 strcpy( lightmapName, "$lightmap" );
3348 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3351 /* get rgbgen string */
3352 if ( rgbGenValues[ style ] == NULL ) {
3353 sprintf( key, "_style%drgbgen", style );
3354 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3355 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3356 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3360 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3361 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3367 /* get alphagen string */
3368 if ( alphaGenValues[ style ] == NULL ) {
3369 sprintf( key, "_style%dalphagen", style );
3370 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3372 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3373 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3376 alphaGen[ 0 ] = '\0';
3379 /* calculate st offset */
3380 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3381 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3383 /* create additional stage */
3384 if ( lmx == 0.0f && lmy == 0.0f ) {
3385 sprintf( styleStage, "\t{\n"
3386 "\t\tmap %s\n" /* lightmap */
3387 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3388 "%s" /* depthFunc equal */
3391 "\t\ttcGen lightmap\n"
3394 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3400 sprintf( styleStage, "\t{\n"
3401 "\t\tmap %s\n" /* lightmap */
3402 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3403 "%s" /* depthFunc equal */
3406 "\t\ttcGen lightmap\n"
3407 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3410 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3418 strcat( styleStages, styleStage );
3421 /* create custom shader */
3422 if ( info->si->styleMarker == 2 ) {
3423 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3426 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3429 /* emit remap command */
3430 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3433 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3434 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3435 //% Sys_Printf( ")\n" );
3438 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3439 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3440 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3441 /* get output lightmap */
3442 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3444 /* do some name mangling */
3445 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3447 /* create custom shader */
3448 csi = CustomShader( info->si, "$lightmap", lightmapName );
3451 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3452 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3453 //% Sys_Printf( ")\n" );
3456 /* use the normal plain-jane shader */
3458 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3463 Sys_Printf( "done.\n" );
3465 /* calc num stored */
3466 numStored = numBSPLightBytes / 3;
3467 efficiency = ( numStored <= 0 )
3469 : (float) numUsed / (float) numStored;
3472 Sys_Printf( "%9d luxels used\n", numUsed );
3473 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3474 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3475 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3476 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3477 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3478 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3479 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3480 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3482 /* write map shader file */
3483 WriteMapShaderFile();