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 ) {
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_FPrintf( SYS_WRN, "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, qboolean fastAllocate ){
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;
1987 int xIncrement, yIncrement;
1990 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1991 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1992 lm->outLightmapNums[ lightmapNum ] = -3;
1994 /* can this lightmap be approximated with vertex color? */
1995 if ( ApproximateLightmap( lm ) ) {
2000 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2003 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
2007 /* don't store twinned lightmaps */
2008 if ( lm->twins[ lightmapNum ] != NULL ) {
2012 /* if this is a styled lightmap, try some normalized locations first */
2014 if ( lightmapNum > 0 && outLightmaps != NULL ) {
2016 for ( j = 0; j < 2; j++ )
2018 /* try identical position */
2019 for ( i = 0; i < numOutLightmaps; i++ )
2021 /* get the output lightmap */
2022 olm = &outLightmaps[ i ];
2024 /* simple early out test */
2025 if ( olm->freeLuxels < lm->used ) {
2029 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2030 if ( olm->customWidth != lm->customWidth ||
2031 olm->customHeight != lm->customHeight ) {
2037 x = lm->lightmapX[ 0 ];
2038 y = lm->lightmapY[ 0 ];
2039 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2045 for ( sy = -1; sy <= 1; sy++ )
2047 for ( sx = -1; sx <= 1; sx++ )
2049 x = lm->lightmapX[ 0 ] + sx * ( olm->customWidth >> 1 ); //% lm->w;
2050 y = lm->lightmapY[ 0 ] + sy * ( olm->customHeight >> 1 ); //% lm->h;
2051 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2075 /* try normal placement algorithm */
2076 if ( ok == qfalse ) {
2081 /* walk the list of lightmap pages */
2082 if ( lightmapSearchBlockSize <= 0 || numOutLightmaps < LIGHTMAP_RESERVE_COUNT ) {
2086 i = ( ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) / lightmapSearchBlockSize ) * lightmapSearchBlockSize;
2088 for ( ; i < numOutLightmaps; i++ )
2090 /* get the output lightmap */
2091 olm = &outLightmaps[ i ];
2093 /* simple early out test */
2094 if ( olm->freeLuxels < lm->used ) {
2098 /* if fast allocation, skip lightmap files that are more than 90% complete */
2099 if ( fastAllocate == qtrue ) {
2100 if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
2105 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2106 if ( olm->customWidth != lm->customWidth ||
2107 olm->customHeight != lm->customHeight ) {
2112 if ( lm->solid[ lightmapNum ] ) {
2113 xMax = olm->customWidth;
2114 yMax = olm->customHeight;
2118 xMax = ( olm->customWidth - lm->w ) + 1;
2119 yMax = ( olm->customHeight - lm->h ) + 1;
2122 /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
2123 if ( fastAllocate == qtrue ) {
2124 xIncrement = MAX(1, lm->w / 15);
2125 yIncrement = MAX(1, lm->h / 15);
2132 /* walk the origin around the lightmap */
2133 for ( y = 0; y < yMax; y += yIncrement )
2135 for ( x = 0; x < xMax; x += xIncrement )
2137 /* find a fine tract of lauhnd */
2138 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2161 if ( ok == qfalse ) {
2162 /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2163 numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2164 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2165 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2166 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2167 free( outLightmaps );
2171 /* initialize both out lightmaps */
2172 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2173 SetupOutLightmap( lm, &outLightmaps[ k ] );
2175 /* set out lightmap */
2176 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2177 olm = &outLightmaps[ i ];
2179 /* set stamp xy origin to the first surface lightmap */
2180 if ( lightmapNum > 0 ) {
2181 x = lm->lightmapX[ 0 ];
2182 y = lm->lightmapY[ 0 ];
2186 /* if this is a style-using lightmap, it must be exported */
2187 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2188 olm->extLightmapNum = 0;
2191 /* add the surface lightmap to the bsp lightmap */
2192 lm->outLightmapNums[ lightmapNum ] = i;
2193 lm->lightmapX[ lightmapNum ] = x;
2194 lm->lightmapY[ lightmapNum ] = y;
2195 olm->numLightmaps++;
2198 for ( i = 0; i < lm->numLightSurfaces; i++ )
2200 /* get surface info */
2201 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2203 /* test for shader */
2204 for ( j = 0; j < olm->numShaders; j++ )
2206 if ( olm->shaders[ j ] == info->si ) {
2211 /* if it doesn't exist, add it */
2212 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2213 olm->shaders[ olm->numShaders ] = info->si;
2215 numLightmapShaders++;
2220 if ( lm->solid[ lightmapNum ] ) {
2230 /* mark the bits used */
2231 for ( y = 0; y < yMax; y++ )
2233 for ( x = 0; x < xMax; x++ )
2236 luxel = BSP_LUXEL( lightmapNum, x, y );
2237 deluxel = BSP_DELUXEL( x, y );
2238 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2242 /* set minimum light */
2243 if ( lm->solid[ lightmapNum ] ) {
2245 VectorSet( color, 255.0f, 0.0f, 0.0f );
2248 VectorCopy( lm->solidColor[ lightmapNum ], color );
2252 VectorCopy( luxel, color );
2255 /* styles are not affected by minlight */
2256 if ( lightmapNum == 0 ) {
2257 for ( i = 0; i < 3; i++ )
2259 if ( color[ i ] < minLight[ i ] ) {
2260 color[ i ] = minLight[ i ];
2265 /* get bsp lightmap coords */
2266 ox = x + lm->lightmapX[ lightmapNum ];
2267 oy = y + lm->lightmapY[ lightmapNum ];
2268 offset = ( oy * olm->customWidth ) + ox;
2270 /* flag pixel as used */
2271 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2275 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2276 ColorToBytes( color, pixel, lm->brightness );
2278 /* store direction */
2280 /* normalize average light direction */
2281 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2282 VectorScale( deluxel, 1000.0f, direction );
2283 VectorNormalize( direction, direction );
2284 VectorScale( direction, 127.5f, direction );
2285 for ( i = 0; i < 3; i++ )
2286 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2296 CompareRawLightmap()
2297 compare function for qsort()
2300 static int CompareRawLightmap( const void *a, const void *b ){
2301 rawLightmap_t *alm, *blm;
2302 surfaceInfo_t *aInfo, *bInfo;
2307 alm = &rawLightmaps[ *( (const int*) a ) ];
2308 blm = &rawLightmaps[ *( (const int*) b ) ];
2310 /* get min number of surfaces */
2311 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2314 for ( i = 0; i < min; i++ )
2316 /* get surface info */
2317 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2318 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2320 /* compare shader names */
2321 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2327 /* test style count */
2329 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2330 diff += blm->styles[ i ] - alm->styles[ i ];
2336 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2341 /* must be equivalent */
2347 void FillOutLightmap( outLightmap_t *olm ){
2350 vec3_t dir_sum, light_sum;
2352 byte *lightBitsNew = NULL;
2353 byte *lightBytesNew = NULL;
2354 byte *dirBytesNew = NULL;
2356 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2357 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2359 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2363 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2364 olm->lightBits[0] |= 1;
2365 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2366 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2367 olm->bspLightBytes[0] = 255;
2368 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2371 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2372 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2374 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2380 for ( y = 0; y < olm->customHeight; ++y )
2382 for ( x = 0; x < olm->customWidth; ++x )
2384 ofs = y * olm->customWidth + x;
2385 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2389 VectorClear( dir_sum );
2390 VectorClear( light_sum );
2392 /* try all four neighbors */
2393 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2394 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2396 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2398 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2402 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2403 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2405 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2407 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2411 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2412 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2414 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2416 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2420 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2421 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2423 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2425 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2431 ofs = y * olm->customWidth + x;
2432 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2433 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2435 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2445 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2446 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2448 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2452 free( lightBitsNew );
2453 free( lightBytesNew );
2455 free( dirBytesNew );
2462 StoreSurfaceLightmaps()
2463 stores the surface lightmaps into the bsp as byte rgb triplets
2466 void StoreSurfaceLightmaps( qboolean fastAllocate ){
2467 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2468 int style, size, lightmapNum, lightmapNum2;
2469 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2470 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2471 float *deluxel, *bspDeluxel, *bspDeluxel2;
2473 int numUsed, numTwins, numTwinLuxels, numStored;
2474 float lmx, lmy, efficiency;
2476 bspDrawSurface_t *ds, *parent, dsTemp;
2477 surfaceInfo_t *info;
2478 rawLightmap_t *lm, *lm2;
2480 bspDrawVert_t *dv, *ydv, *dvParent;
2481 char dirname[ 1024 ], filename[ 1024 ];
2483 char lightmapName[ 128 ];
2484 const char *rgbGenValues[ 256 ];
2485 const char *alphaGenValues[ 256 ];
2489 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2492 if ( lmCustomDir ) {
2493 strcpy( dirname, lmCustomDir );
2497 strcpy( dirname, source );
2498 StripExtension( dirname );
2500 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2501 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2503 /* -----------------------------------------------------------------
2504 average the sampled luxels into the bsp luxels
2505 ----------------------------------------------------------------- */
2508 Sys_Printf( "Subsampling..." );
2510 /* walk the list of raw lightmaps */
2514 numSolidLightmaps = 0;
2515 for ( i = 0; i < numRawLightmaps; i++ )
2518 lm = &rawLightmaps[ i ];
2520 /* walk individual lightmaps */
2521 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2524 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2528 /* allocate bsp luxel storage */
2529 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2530 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2531 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2532 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2535 /* allocate radiosity lightmap storage */
2537 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2538 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2539 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2541 memset( lm->radLuxels[ lightmapNum ], 0, size );
2544 /* average supersampled luxels */
2545 for ( y = 0; y < lm->h; y++ )
2547 for ( x = 0; x < lm->w; x++ )
2551 occludedSamples = 0.0f;
2553 VectorClear( sample );
2554 VectorClear( occludedSample );
2555 VectorClear( dirSample );
2556 for ( ly = 0; ly < superSample; ly++ )
2558 for ( lx = 0; lx < superSample; lx++ )
2561 sx = x * superSample + lx;
2562 sy = y * superSample + ly;
2563 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2564 deluxel = SUPER_DELUXEL( sx, sy );
2565 normal = SUPER_NORMAL( sx, sy );
2566 cluster = SUPER_CLUSTER( sx, sy );
2568 /* sample deluxemap */
2569 if ( deluxemap && lightmapNum == 0 ) {
2570 VectorAdd( dirSample, deluxel, dirSample );
2573 /* keep track of used/occluded samples */
2574 if ( *cluster != CLUSTER_UNMAPPED ) {
2578 /* handle lightmap border? */
2579 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2580 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2585 else if ( debug && *cluster < 0 ) {
2586 if ( *cluster == CLUSTER_UNMAPPED ) {
2587 VectorSet( luxel, 255, 204, 0 );
2589 else if ( *cluster == CLUSTER_OCCLUDED ) {
2590 VectorSet( luxel, 255, 0, 255 );
2592 else if ( *cluster == CLUSTER_FLOODED ) {
2593 VectorSet( luxel, 0, 32, 255 );
2595 VectorAdd( occludedSample, luxel, occludedSample );
2596 occludedSamples += 1.0f;
2599 /* normal luxel handling */
2600 else if ( luxel[ 3 ] > 0.0f ) {
2601 /* handle lit or flooded luxels */
2602 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2603 VectorAdd( sample, luxel, sample );
2604 samples += luxel[ 3 ];
2607 /* handle occluded or unmapped luxels */
2610 VectorAdd( occludedSample, luxel, occludedSample );
2611 occludedSamples += luxel[ 3 ];
2614 /* handle style debugging */
2615 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2616 VectorCopy( debugColors[ 0 ], sample );
2623 /* only use occluded samples if necessary */
2624 if ( samples <= 0.0f ) {
2625 VectorCopy( occludedSample, sample );
2626 samples = occludedSamples;
2630 luxel = SUPER_LUXEL( lightmapNum, x, y );
2631 deluxel = SUPER_DELUXEL( x, y );
2633 /* store light direction */
2634 if ( deluxemap && lightmapNum == 0 ) {
2635 VectorCopy( dirSample, deluxel );
2638 /* store the sample back in super luxels */
2639 if ( samples > 0.01f ) {
2640 VectorScale( sample, ( 1.0f / samples ), luxel );
2644 /* if any samples were mapped in any way, store ambient color */
2645 else if ( mappedSamples > 0 ) {
2646 if ( lightmapNum == 0 ) {
2647 VectorCopy( ambientColor, luxel );
2650 VectorClear( luxel );
2655 /* store a bogus value to be fixed later */
2658 VectorClear( luxel );
2666 ClearBounds( colorMins, colorMaxs );
2668 /* clean up and store into bsp luxels */
2669 for ( y = 0; y < lm->h; y++ )
2671 for ( x = 0; x < lm->w; x++ )
2674 luxel = SUPER_LUXEL( lightmapNum, x, y );
2675 deluxel = SUPER_DELUXEL( x, y );
2677 /* copy light direction */
2678 if ( deluxemap && lightmapNum == 0 ) {
2679 VectorCopy( deluxel, dirSample );
2682 /* is this a valid sample? */
2683 if ( luxel[ 3 ] > 0.0f ) {
2684 VectorCopy( luxel, sample );
2685 samples = luxel[ 3 ];
2689 /* fix negative samples */
2690 for ( j = 0; j < 3; j++ )
2692 if ( sample[ j ] < 0.0f ) {
2699 /* nick an average value from the neighbors */
2700 VectorClear( sample );
2701 VectorClear( dirSample );
2704 /* fixme: why is this disabled?? */
2705 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2707 if ( sy < 0 || sy >= lm->h ) {
2711 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2713 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2717 /* get neighbor's particulars */
2718 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2719 if ( luxel[ 3 ] < 0.0f ) {
2722 VectorAdd( sample, luxel, sample );
2723 samples += luxel[ 3 ];
2728 if ( samples == 0.0f ) {
2729 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2737 /* fix negative samples */
2738 for ( j = 0; j < 3; j++ )
2740 if ( sample[ j ] < 0.0f ) {
2747 /* scale the sample */
2748 VectorScale( sample, ( 1.0f / samples ), sample );
2750 /* store the sample in the radiosity luxels */
2752 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2753 VectorCopy( sample, radLuxel );
2755 /* if only storing bounced light, early out here */
2756 if ( bounceOnly && !bouncing ) {
2761 /* store the sample in the bsp luxels */
2762 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2763 bspDeluxel = BSP_DELUXEL( x, y );
2765 VectorAdd( bspLuxel, sample, bspLuxel );
2766 if ( deluxemap && lightmapNum == 0 ) {
2767 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2770 /* add color to bounds for solid checking */
2771 if ( samples > 0.0f ) {
2772 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2777 /* set solid color */
2778 lm->solid[ lightmapNum ] = qfalse;
2779 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2780 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2782 /* nocollapse prevents solid lightmaps */
2783 if ( noCollapse == qfalse ) {
2784 /* check solid color */
2785 VectorSubtract( colorMaxs, colorMins, sample );
2786 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2787 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2789 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2790 lm->solid[ lightmapNum ] = qtrue;
2791 numSolidLightmaps++;
2794 /* if all lightmaps aren't solid, then none of them are solid */
2795 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2796 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2798 if ( lm->solid[ y ] ) {
2799 numSolidLightmaps--;
2801 lm->solid[ y ] = qfalse;
2806 /* wrap bsp luxels if necessary */
2807 if ( lm->wrap[ 0 ] ) {
2808 for ( y = 0; y < lm->h; y++ )
2810 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2811 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2812 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2813 VectorScale( bspLuxel, 0.5f, bspLuxel );
2814 VectorCopy( bspLuxel, bspLuxel2 );
2815 if ( deluxemap && lightmapNum == 0 ) {
2816 bspDeluxel = BSP_DELUXEL( 0, y );
2817 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2818 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2819 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2820 VectorCopy( bspDeluxel, bspDeluxel2 );
2824 if ( lm->wrap[ 1 ] ) {
2825 for ( x = 0; x < lm->w; x++ )
2827 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2828 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2829 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2830 VectorScale( bspLuxel, 0.5f, bspLuxel );
2831 VectorCopy( bspLuxel, bspLuxel2 );
2832 if ( deluxemap && lightmapNum == 0 ) {
2833 bspDeluxel = BSP_DELUXEL( x, 0 );
2834 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2835 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2836 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2837 VectorCopy( bspDeluxel, bspDeluxel2 );
2844 /* -----------------------------------------------------------------
2845 convert modelspace deluxemaps to tangentspace
2846 ----------------------------------------------------------------- */
2849 if ( deluxemap && deluxemode == 1 ) {
2850 vec3_t worldUp, myNormal, myTangent, myBinormal;
2853 Sys_Printf( "converting..." );
2855 for ( i = 0; i < numRawLightmaps; i++ )
2858 lm = &rawLightmaps[ i ];
2860 /* walk lightmap samples */
2861 for ( y = 0; y < lm->sh; y++ )
2863 for ( x = 0; x < lm->sw; x++ )
2865 /* get normal and deluxel */
2866 normal = SUPER_NORMAL( x, y );
2867 cluster = SUPER_CLUSTER( x, y );
2868 bspDeluxel = BSP_DELUXEL( x, y );
2869 deluxel = SUPER_DELUXEL( x, y );
2872 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2874 /* get tangent vectors */
2875 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2876 if ( myNormal[ 2 ] == 1.0f ) {
2877 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2878 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2880 else if ( myNormal[ 2 ] == -1.0f ) {
2881 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2882 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2887 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2888 CrossProduct( myNormal, worldUp, myTangent );
2889 VectorNormalize( myTangent, myTangent );
2890 CrossProduct( myTangent, myNormal, myBinormal );
2891 VectorNormalize( myBinormal, myBinormal );
2894 /* project onto plane */
2895 dist = -DotProduct( myTangent, myNormal );
2896 VectorMA( myTangent, dist, myNormal, myTangent );
2897 dist = -DotProduct( myBinormal, myNormal );
2898 VectorMA( myBinormal, dist, myNormal, myBinormal );
2901 VectorNormalize( myTangent, myTangent );
2902 VectorNormalize( myBinormal, myBinormal );
2904 /* convert modelspace deluxel to tangentspace */
2905 dirSample[0] = bspDeluxel[0];
2906 dirSample[1] = bspDeluxel[1];
2907 dirSample[2] = bspDeluxel[2];
2908 VectorNormalize( dirSample, dirSample );
2910 /* fix tangents to world matrix */
2911 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2912 VectorNegate( myTangent, myTangent );
2915 /* build tangentspace vectors */
2916 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2917 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2918 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2925 /* -----------------------------------------------------------------
2927 ----------------------------------------------------------------- */
2929 #ifdef sdfsdfwq312323
2931 Sys_Printf( "blending..." );
2933 for ( i = 0; i < numRawLightmaps; i++ )
2939 lm = &rawLightmaps[ i ];
2941 /* walk individual lightmaps */
2942 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2945 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2949 /* walk lightmap samples */
2950 for ( y = 0; y < lm->sh; y++ )
2952 for ( x = 0; x < lm->sw; x++ )
2955 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2958 VectorNormalize( bspLuxel, myColor );
2959 myBrightness = VectorLength( bspLuxel );
2960 myBrightness *= ( 1 / 127.0f );
2961 myBrightness = myBrightness * myBrightness;
2962 myBrightness *= 127.0f;
2963 VectorScale( myColor, myBrightness, bspLuxel );
2970 /* -----------------------------------------------------------------
2971 collapse non-unique lightmaps
2972 ----------------------------------------------------------------- */
2974 if ( noCollapse == qfalse && deluxemap == qfalse ) {
2976 Sys_Printf( "collapsing..." );
2978 /* set all twin refs to null */
2979 for ( i = 0; i < numRawLightmaps; i++ )
2981 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2983 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2984 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2985 rawLightmaps[ i ].numStyledTwins = 0;
2989 /* walk the list of raw lightmaps */
2990 for ( i = 0; i < numRawLightmaps; i++ )
2993 lm = &rawLightmaps[ i ];
2995 /* walk lightmaps */
2996 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2999 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
3000 lm->twins[ lightmapNum ] != NULL ) {
3004 /* find all lightmaps that are virtually identical to this one */
3005 for ( j = i + 1; j < numRawLightmaps; j++ )
3008 lm2 = &rawLightmaps[ j ];
3010 /* walk lightmaps */
3011 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3014 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3015 lm2->twins[ lightmapNum2 ] != NULL ) {
3020 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3021 /* merge and set twin */
3022 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3023 lm2->twins[ lightmapNum2 ] = lm;
3024 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3026 numTwinLuxels += ( lm->w * lm->h );
3028 /* count styled twins */
3029 if ( lightmapNum > 0 ) {
3030 lm->numStyledTwins++;
3040 /* -----------------------------------------------------------------
3041 sort raw lightmaps by shader
3042 ----------------------------------------------------------------- */
3045 Sys_Printf( "sorting..." );
3047 /* allocate a new sorted list */
3048 if ( sortLightmaps == NULL ) {
3049 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3052 /* fill it out and sort it */
3053 for ( i = 0; i < numRawLightmaps; i++ )
3054 sortLightmaps[ i ] = i;
3055 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3057 /* -----------------------------------------------------------------
3058 allocate output lightmaps
3059 ----------------------------------------------------------------- */
3062 Sys_Printf( "allocating..." );
3064 /* kill all existing output lightmaps */
3065 if ( outLightmaps != NULL ) {
3066 for ( i = 0; i < numOutLightmaps; i++ )
3068 free( outLightmaps[ i ].lightBits );
3069 free( outLightmaps[ i ].bspLightBytes );
3071 free( outLightmaps );
3072 outLightmaps = NULL;
3075 numLightmapShaders = 0;
3076 numOutLightmaps = 0;
3077 numBSPLightmaps = 0;
3078 numExtLightmaps = 0;
3080 /* find output lightmap */
3081 for ( i = 0; i < numRawLightmaps; i++ )
3083 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3084 FindOutLightmaps( lm, fastAllocate );
3087 /* set output numbers in twinned lightmaps */
3088 for ( i = 0; i < numRawLightmaps; i++ )
3091 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3093 /* walk lightmaps */
3094 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3097 lm2 = lm->twins[ lightmapNum ];
3098 if ( lm2 == NULL ) {
3101 lightmapNum2 = lm->twinNums[ lightmapNum ];
3103 /* find output lightmap from twin */
3104 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3105 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3106 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3110 /* -----------------------------------------------------------------
3111 store output lightmaps
3112 ----------------------------------------------------------------- */
3115 Sys_Printf( "storing..." );
3117 /* count the bsp lightmaps and allocate space */
3118 if ( bspLightBytes != NULL ) {
3119 free( bspLightBytes );
3121 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3122 numBSPLightBytes = 0;
3123 bspLightBytes = NULL;
3127 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3128 bspLightBytes = safe_malloc( numBSPLightBytes );
3129 memset( bspLightBytes, 0, numBSPLightBytes );
3132 /* walk the list of output lightmaps */
3133 for ( i = 0; i < numOutLightmaps; i++ )
3135 /* get output lightmap */
3136 olm = &outLightmaps[ i ];
3138 /* fill output lightmap */
3139 if ( lightmapFill ) {
3140 FillOutLightmap( olm );
3143 /* is this a valid bsp lightmap? */
3144 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3145 /* copy lighting data */
3146 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3147 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3149 /* copy direction data */
3151 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3152 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3156 /* external lightmap? */
3157 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3158 /* make a directory for the lightmaps */
3161 /* set external lightmap number */
3162 olm->extLightmapNum = numExtLightmaps;
3164 /* write lightmap */
3165 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3166 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3167 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3170 /* write deluxemap */
3172 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3173 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3174 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3177 if ( debugDeluxemap ) {
3178 olm->extLightmapNum++;
3184 if ( numExtLightmaps > 0 ) {
3188 /* delete unused external lightmaps */
3189 for ( i = numExtLightmaps; i; i++ )
3191 /* determine if file exists */
3192 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3193 if ( !FileExists( filename ) ) {
3201 /* -----------------------------------------------------------------
3202 project the lightmaps onto the bsp surfaces
3203 ----------------------------------------------------------------- */
3206 Sys_Printf( "projecting..." );
3208 /* walk the list of surfaces */
3209 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3211 /* get the surface and info */
3212 ds = &bspDrawSurfaces[ i ];
3213 info = &surfaceInfos[ i ];
3217 /* handle surfaces with identical parent */
3218 if ( info->parentSurfaceNum >= 0 ) {
3219 /* preserve original data and get parent */
3220 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3221 memcpy( &dsTemp, ds, sizeof( *ds ) );
3223 /* overwrite child with parent data */
3224 memcpy( ds, parent, sizeof( *ds ) );
3226 /* restore key parts */
3227 ds->fogNum = dsTemp.fogNum;
3228 ds->firstVert = dsTemp.firstVert;
3229 ds->firstIndex = dsTemp.firstIndex;
3230 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3232 /* set vertex data */
3233 dv = &bspDrawVerts[ ds->firstVert ];
3234 dvParent = &bspDrawVerts[ parent->firstVert ];
3235 for ( j = 0; j < ds->numVerts; j++ )
3237 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3238 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3245 /* handle vertex lit or approximated surfaces */
3246 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3247 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3249 ds->lightmapNum[ lightmapNum ] = -3;
3250 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3254 /* handle lightmapped surfaces */
3257 /* walk lightmaps */
3258 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3261 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3263 /* handle unused style */
3264 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3265 ds->lightmapNum[ lightmapNum ] = -3;
3269 /* get output lightmap */
3270 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3272 /* set bsp lightmap number */
3273 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3275 /* deluxemap debugging makes the deluxemap visible */
3276 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3277 ds->lightmapNum[ lightmapNum ]++;
3280 /* calc lightmap origin in texture space */
3281 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3282 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3284 /* calc lightmap st coords */
3285 dv = &bspDrawVerts[ ds->firstVert ];
3286 ydv = &yDrawVerts[ ds->firstVert ];
3287 for ( j = 0; j < ds->numVerts; j++ )
3289 if ( lm->solid[ lightmapNum ] ) {
3290 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3291 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3295 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3296 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3302 /* store vertex colors */
3303 dv = &bspDrawVerts[ ds->firstVert ];
3304 for ( j = 0; j < ds->numVerts; j++ )
3306 /* walk lightmaps */
3307 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3309 /* handle unused style */
3310 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3311 VectorClear( color );
3315 /* get vertex color */
3316 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3317 VectorCopy( luxel, color );
3319 /* set minimum light */
3320 if ( lightmapNum == 0 ) {
3321 for ( k = 0; k < 3; k++ )
3322 if ( color[ k ] < minVertexLight[ k ] ) {
3323 color[ k ] = minVertexLight[ k ];
3328 /* store to bytes */
3329 if ( !info->si->noVertexLight ) {
3330 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3335 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3336 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3338 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3342 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3343 dv = &bspDrawVerts[ ds->firstVert ];
3345 /* depthFunc equal? */
3346 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3353 /* generate stages for styled lightmaps */
3354 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3357 style = lm->styles[ lightmapNum ];
3358 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3362 /* get output lightmap */
3363 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3366 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3367 strcpy( lightmapName, "$lightmap" );
3370 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3373 /* get rgbgen string */
3374 if ( rgbGenValues[ style ] == NULL ) {
3375 sprintf( key, "_style%drgbgen", style );
3376 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3377 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3378 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3382 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3383 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3389 /* get alphagen string */
3390 if ( alphaGenValues[ style ] == NULL ) {
3391 sprintf( key, "_style%dalphagen", style );
3392 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3394 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3395 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3398 alphaGen[ 0 ] = '\0';
3401 /* calculate st offset */
3402 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3403 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3405 /* create additional stage */
3406 if ( lmx == 0.0f && lmy == 0.0f ) {
3407 sprintf( styleStage, "\t{\n"
3408 "\t\tmap %s\n" /* lightmap */
3409 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3410 "%s" /* depthFunc equal */
3413 "\t\ttcGen lightmap\n"
3416 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3422 sprintf( styleStage, "\t{\n"
3423 "\t\tmap %s\n" /* lightmap */
3424 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3425 "%s" /* depthFunc equal */
3428 "\t\ttcGen lightmap\n"
3429 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3432 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3440 strcat( styleStages, styleStage );
3443 /* create custom shader */
3444 if ( info->si->styleMarker == 2 ) {
3445 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3448 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3451 /* emit remap command */
3452 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3455 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3456 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3457 //% Sys_Printf( ")\n" );
3460 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3461 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3462 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3463 /* get output lightmap */
3464 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3466 /* do some name mangling */
3467 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3469 /* create custom shader */
3470 csi = CustomShader( info->si, "$lightmap", lightmapName );
3473 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3474 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3475 //% Sys_Printf( ")\n" );
3478 /* use the normal plain-jane shader */
3480 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3485 Sys_Printf( "done.\n" );
3487 /* calc num stored */
3488 numStored = numBSPLightBytes / 3;
3489 efficiency = ( numStored <= 0 )
3491 : (float) numUsed / (float) numStored;
3494 Sys_Printf( "%9d luxels used\n", numUsed );
3495 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3496 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3497 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3498 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3499 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3500 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3501 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3502 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3504 /* write map shader file */
3505 WriteMapShaderFile();