1 /* -------------------------------------------------------------------------------
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
6 This file is part of GtkRadiant.
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 ----------------------------------------------------------------------------------
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
27 ------------------------------------------------------------------------------- */
32 #define LIGHTMAPS_YDNAR_C
42 /* -------------------------------------------------------------------------------
44 this file contains code that doe lightmap allocation and projection that
45 runs in the -light phase.
47 this is handled here rather than in the bsp phase for a few reasons--
48 surfaces are no longer necessarily convex polygons, patches may or may not be
49 planar or have lightmaps projected directly onto control points.
51 also, this allows lightmaps to be calculated before being allocated and stored
52 in the bsp. lightmaps that have little high-frequency information are candidates
53 for having their resolutions scaled down.
55 ------------------------------------------------------------------------------- */
59 based on WriteTGA() from imagelib.c
62 void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip ){
68 /* allocate a buffer and set it up */
69 buffer = safe_malloc( width * height * 3 + 18 );
70 memset( buffer, 0, 18 );
72 buffer[ 12 ] = width & 255;
73 buffer[ 13 ] = width >> 8;
74 buffer[ 14 ] = height & 255;
75 buffer[ 15 ] = height >> 8;
79 c = ( width * height * 3 ) + 18;
80 for ( i = 18; i < c; i += 3 )
82 buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
83 buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
84 buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
87 /* write it and free the buffer */
88 file = fopen( filename, "wb" );
90 Error( "Unable to open %s for writing", filename );
93 /* flip vertically? */
95 fwrite( buffer, 1, 18, file );
96 for ( in = buffer + ( ( height - 1 ) * width * 3 ) + 18; in >= buffer; in -= ( width * 3 ) )
97 fwrite( in, 1, ( width * 3 ), file );
100 fwrite( buffer, 1, c, file );
112 exports the lightmaps as a list of numbered tga images
115 void ExportLightmaps( void ){
117 char dirname[ 1024 ], filename[ 1024 ];
122 Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n" );
124 /* do some path mangling */
125 strcpy( dirname, source );
126 StripExtension( dirname );
129 if ( bspLightBytes == NULL ) {
130 Sys_FPrintf( SYS_WRN, "WARNING: No BSP lightmap data\n" );
134 /* make a directory for the lightmaps */
137 /* iterate through the lightmaps */
138 for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
140 /* write a tga image out */
141 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
142 Sys_Printf( "Writing %s\n", filename );
143 WriteTGA24( filename, lightmap, game->lightmapSize, game->lightmapSize, qfalse );
150 ExportLightmapsMain()
151 exports the lightmaps as a list of numbered tga images
154 int ExportLightmapsMain( int argc, char **argv ){
157 Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
161 /* do some path mangling */
162 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
163 StripExtension( source );
164 DefaultExtension( source, ".bsp" );
167 Sys_Printf( "Loading %s\n", source );
168 LoadBSPFile( source );
170 /* export the lightmaps */
173 /* return to sender */
180 ImportLightmapsMain()
181 imports the lightmaps from a list of numbered tga images
184 int ImportLightmapsMain( int argc, char **argv ){
185 int i, x, y, len, width, height;
186 char dirname[ 1024 ], filename[ 1024 ];
187 byte *lightmap, *buffer, *pixels, *in, *out;
192 Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
196 /* do some path mangling */
197 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
198 StripExtension( source );
199 DefaultExtension( source, ".bsp" );
202 Sys_Printf( "Loading %s\n", source );
203 LoadBSPFile( source );
206 Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n" );
208 /* do some path mangling */
209 strcpy( dirname, source );
210 StripExtension( dirname );
213 if ( bspLightBytes == NULL ) {
214 Error( "No lightmap data" );
217 /* make a directory for the lightmaps */
220 /* iterate through the lightmaps */
221 for ( i = 0, lightmap = bspLightBytes; lightmap < ( bspLightBytes + numBSPLightBytes ); i++, lightmap += ( game->lightmapSize * game->lightmapSize * 3 ) )
223 /* read a tga image */
224 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
225 Sys_Printf( "Loading %s\n", filename );
227 len = vfsLoadFile( filename, (void*) &buffer, -1 );
229 Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
233 /* parse file into an image */
235 LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
238 /* sanity check it */
239 if ( pixels == NULL ) {
240 Sys_FPrintf( SYS_WRN, "WARNING: Unable to load image %s\n", filename );
243 if ( width != game->lightmapSize || height != game->lightmapSize ) {
244 Sys_FPrintf( SYS_WRN, "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
245 filename, width, height, game->lightmapSize, game->lightmapSize );
248 /* copy the pixels */
250 for ( y = 1; y <= game->lightmapSize; y++ )
252 out = lightmap + ( ( game->lightmapSize - y ) * game->lightmapSize * 3 );
253 for ( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
254 VectorCopy( in, out );
262 Sys_Printf( "writing %s\n", source );
263 WriteBSPFile( source );
265 /* return to sender */
271 /* -------------------------------------------------------------------------------
273 this section deals with projecting a lightmap onto a raw drawsurface
275 ------------------------------------------------------------------------------- */
278 CompareLightSurface()
279 compare function for qsort()
282 static int CompareLightSurface( const void *a, const void *b ){
283 shaderInfo_t *asi, *bsi;
287 asi = surfaceInfos[ *( (const int*) a ) ].si;
288 bsi = surfaceInfos[ *( (const int*) b ) ].si;
298 /* compare shader names */
299 return strcmp( asi->shader, bsi->shader );
306 allocates a raw lightmap's necessary buffers
309 void FinishRawLightmap( rawLightmap_t *lm ){
310 int i, j, c, size, *sc;
315 /* sort light surfaces by shader name */
316 qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
319 lm->numLightClusters = 0;
320 for ( i = 0; i < lm->numLightSurfaces; i++ )
322 /* get surface info */
323 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
325 /* add surface clusters */
326 lm->numLightClusters += info->numSurfaceClusters;
329 /* allocate buffer for clusters and copy */
330 lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
332 for ( i = 0; i < lm->numLightSurfaces; i++ )
334 /* get surface info */
335 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
337 /* add surface clusters */
338 for ( j = 0; j < info->numSurfaceClusters; j++ )
339 lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
343 lm->styles[ 0 ] = LS_NORMAL;
344 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
345 lm->styles[ i ] = LS_NONE;
347 /* set supersampling size */
348 lm->sw = lm->w * superSample;
349 lm->sh = lm->h * superSample;
351 /* add to super luxel count */
352 numRawSuperLuxels += ( lm->sw * lm->sh );
354 /* manipulate origin/vecs for supersampling */
355 if ( superSample > 1 && lm->vecs != NULL ) {
356 /* calc inverse supersample */
357 is = 1.0f / superSample;
359 /* scale the vectors and shift the origin */
361 /* new code that works for arbitrary supersampling values */
362 VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
363 VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
364 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
365 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
366 VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
367 VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
369 /* old code that only worked with a value of 2 */
370 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
371 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
372 VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
373 VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
377 /* allocate bsp lightmap storage */
378 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
379 if ( lm->bspLuxels[ 0 ] == NULL ) {
380 lm->bspLuxels[ 0 ] = safe_malloc( size );
382 memset( lm->bspLuxels[ 0 ], 0, size );
384 /* allocate radiosity lightmap storage */
386 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
387 if ( lm->radLuxels[ 0 ] == NULL ) {
388 lm->radLuxels[ 0 ] = safe_malloc( size );
390 memset( lm->radLuxels[ 0 ], 0, size );
393 /* allocate sampling lightmap storage */
394 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
395 if ( lm->superLuxels[ 0 ] == NULL ) {
396 lm->superLuxels[ 0 ] = safe_malloc( size );
398 memset( lm->superLuxels[ 0 ], 0, size );
400 /* allocate origin map storage */
401 size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
402 if ( lm->superOrigins == NULL ) {
403 lm->superOrigins = safe_malloc( size );
405 memset( lm->superOrigins, 0, size );
407 /* allocate normal map storage */
408 size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
409 if ( lm->superNormals == NULL ) {
410 lm->superNormals = safe_malloc( size );
412 memset( lm->superNormals, 0, size );
414 /* allocate floodlight map storage */
415 size = lm->sw * lm->sh * SUPER_FLOODLIGHT_SIZE * sizeof( float );
416 if ( lm->superFloodLight == NULL ) {
417 lm->superFloodLight = safe_malloc( size );
419 memset( lm->superFloodLight, 0, size );
421 /* allocate cluster map storage */
422 size = lm->sw * lm->sh * sizeof( int );
423 if ( lm->superClusters == NULL ) {
424 lm->superClusters = safe_malloc( size );
426 size = lm->sw * lm->sh;
427 sc = lm->superClusters;
428 for ( i = 0; i < size; i++ )
429 ( *sc++ ) = CLUSTER_UNMAPPED;
431 /* deluxemap allocation */
433 /* allocate sampling deluxel storage */
434 size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
435 if ( lm->superDeluxels == NULL ) {
436 lm->superDeluxels = safe_malloc( size );
438 memset( lm->superDeluxels, 0, size );
440 /* allocate bsp deluxel storage */
441 size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
442 if ( lm->bspDeluxels == NULL ) {
443 lm->bspDeluxels = safe_malloc( size );
445 memset( lm->bspDeluxels, 0, size );
449 numLuxels += ( lm->sw * lm->sh );
455 AddPatchToRawLightmap()
456 projects a lightmap for a patch surface
457 since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
458 it is no longer necessary for patch verts to fall exactly on a lightmap sample
459 based on AllocateLightmapForPatch()
462 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm ){
463 bspDrawSurface_t *ds;
466 bspDrawVert_t *verts, *a, *b;
468 mesh_t src, *subdivided, *mesh;
469 float sBasis, tBasis, s, t;
470 float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
473 /* patches finish a raw lightmap */
474 lm->finished = qtrue;
476 /* get surface and info */
477 ds = &bspDrawSurfaces[ num ];
478 info = &surfaceInfos[ num ];
480 /* make a temporary mesh from the drawsurf */
481 src.width = ds->patchWidth;
482 src.height = ds->patchHeight;
483 src.verts = &yDrawVerts[ ds->firstVert ];
484 //% subdivided = SubdivideMesh( src, 8, 512 );
485 subdivided = SubdivideMesh2( src, info->patchIterations );
487 /* fit it to the curve and remove colinear verts on rows/columns */
488 PutMeshOnCurve( *subdivided );
489 mesh = RemoveLinearMeshColumnsRows( subdivided );
490 FreeMesh( subdivided );
492 /* find the longest distance on each row/column */
494 memset( widthTable, 0, sizeof( widthTable ) );
495 memset( heightTable, 0, sizeof( heightTable ) );
496 for ( y = 0; y < mesh->height; y++ )
498 for ( x = 0; x < mesh->width; x++ )
501 if ( x + 1 < mesh->width ) {
502 a = &verts[ ( y * mesh->width ) + x ];
503 b = &verts[ ( y * mesh->width ) + x + 1 ];
504 VectorSubtract( a->xyz, b->xyz, delta );
505 length = VectorLength( delta );
506 if ( length > widthTable[ x ] ) {
507 widthTable[ x ] = length;
512 if ( y + 1 < mesh->height ) {
513 a = &verts[ ( y * mesh->width ) + x ];
514 b = &verts[ ( ( y + 1 ) * mesh->width ) + x ];
515 VectorSubtract( a->xyz, b->xyz, delta );
516 length = VectorLength( delta );
517 if ( length > heightTable[ y ] ) {
518 heightTable[ y ] = length;
524 /* determine lightmap width */
526 for ( x = 0; x < ( mesh->width - 1 ); x++ )
527 length += widthTable[ x ];
528 lm->w = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
529 if ( lm->w < ds->patchWidth ) {
530 lm->w = ds->patchWidth;
532 if ( lm->w > lm->customWidth ) {
533 lm->w = lm->customWidth;
535 sBasis = (float) ( lm->w - 1 ) / (float) ( ds->patchWidth - 1 );
537 /* determine lightmap height */
539 for ( y = 0; y < ( mesh->height - 1 ); y++ )
540 length += heightTable[ y ];
541 lm->h = lm->sampleSize != 0 ? ceil( length / lm->sampleSize ) + 1 : 0;
542 if ( lm->h < ds->patchHeight ) {
543 lm->h = ds->patchHeight;
545 if ( lm->h > lm->customHeight ) {
546 lm->h = lm->customHeight;
548 tBasis = (float) ( lm->h - 1 ) / (float) ( ds->patchHeight - 1 );
550 /* free the temporary mesh */
553 /* set the lightmap texture coordinates in yDrawVerts */
554 lm->wrap[ 0 ] = qtrue;
555 lm->wrap[ 1 ] = qtrue;
556 verts = &yDrawVerts[ ds->firstVert ];
557 for ( y = 0; y < ds->patchHeight; y++ )
559 t = ( tBasis * y ) + 0.5f;
560 for ( x = 0; x < ds->patchWidth; x++ )
562 s = ( sBasis * x ) + 0.5f;
563 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
564 verts[ ( y * ds->patchWidth ) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
566 if ( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ( ( ds->patchHeight - 1 ) * ds->patchWidth ) + x ].xyz ) ) {
567 lm->wrap[ 1 ] = qfalse;
571 if ( !VectorCompare( verts[ ( y * ds->patchWidth ) ].xyz, verts[ ( y * ds->patchWidth ) + ( ds->patchWidth - 1 ) ].xyz ) ) {
572 lm->wrap[ 0 ] = qfalse;
577 //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
578 //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
579 //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
580 //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
581 //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
584 numPatchesLightmapped++;
593 AddSurfaceToRawLightmap()
594 projects a lightmap for a surface
595 based on AllocateLightmapForSurface()
598 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm ){
599 bspDrawSurface_t *ds, *ds2;
601 int num2, n, i, axisNum;
602 float s, t, d, len, sampleSize;
603 vec3_t mins, maxs, origin, faxis, size, delta, normalized, vecs[ 2 ];
605 bspDrawVert_t *verts;
608 /* get surface and info */
609 ds = &bspDrawSurfaces[ num ];
610 info = &surfaceInfos[ num ];
612 /* add the surface to the raw lightmap */
613 lightSurfaces[ numLightSurfaces++ ] = num;
614 lm->numLightSurfaces++;
616 /* does this raw lightmap already have any surfaces? */
617 if ( lm->numLightSurfaces > 1 ) {
618 /* surface and raw lightmap must have the same lightmap projection axis */
619 if ( VectorCompare( info->axis, lm->axis ) == qfalse ) {
623 /* match identical attributes */
624 if ( info->sampleSize != lm->sampleSize ||
625 info->entityNum != lm->entityNum ||
626 info->recvShadows != lm->recvShadows ||
627 info->si->lmCustomWidth != lm->customWidth ||
628 info->si->lmCustomHeight != lm->customHeight ||
629 info->si->lmBrightness != lm->brightness ||
630 info->si->lmFilterRadius != lm->filterRadius ||
631 info->si->splotchFix != lm->splotchFix ) {
635 /* surface bounds must intersect with raw lightmap bounds */
636 for ( i = 0; i < 3; i++ )
638 if ( info->mins[ i ] > lm->maxs[ i ] ) {
641 if ( info->maxs[ i ] < lm->mins[ i ] ) {
646 /* plane check (fixme: allow merging of nonplanars) */
647 if ( info->si->lmMergable == qfalse ) {
648 if ( info->plane == NULL || lm->plane == NULL ) {
653 for ( i = 0; i < 4; i++ )
654 if ( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON ) {
659 /* debug code hacking */
660 //% if( lm->numLightSurfaces > 1 )
665 if ( info->plane == NULL ) {
669 /* add surface to lightmap bounds */
670 AddPointToBounds( info->mins, lm->mins, lm->maxs );
671 AddPointToBounds( info->maxs, lm->mins, lm->maxs );
673 /* check to see if this is a non-planar patch */
674 if ( ds->surfaceType == MST_PATCH &&
675 lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f ) {
676 return AddPatchToRawLightmap( num, lm );
679 /* start with initially requested sample size */
680 sampleSize = lm->sampleSize;
682 /* round to the lightmap resolution */
683 for ( i = 0; i < 3; i++ )
685 mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
686 maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
687 size[ i ] = ( maxs[ i ] - mins[ i ] ) / sampleSize + 1.0f;
689 /* hack (god this sucks) */
690 if ( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight || ( lmLimitSize && size[i] > lmLimitSize ) ) {
696 if ( sampleSize != lm->sampleSize && lmLimitSize == 0 ) {
697 Sys_FPrintf( SYS_VRB,"WARNING: surface at (%6.0f %6.0f %6.0f) (%6.0f %6.0f %6.0f) too large for desired samplesize/lightmapsize/lightmapscale combination, increased samplesize from %d to %d\n",
708 /* set actual sample size */
709 lm->actualSampleSize = sampleSize;
711 /* fixme: copy rounded mins/maxes to lightmap record? */
712 if ( lm->plane == NULL ) {
713 VectorCopy( mins, lm->mins );
714 VectorCopy( maxs, lm->maxs );
715 VectorCopy( mins, origin );
718 /* set lightmap origin */
719 VectorCopy( lm->mins, origin );
721 /* make absolute axis */
722 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
723 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
724 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
726 /* clear out lightmap vectors */
727 memset( vecs, 0, sizeof( vecs ) );
729 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
730 if ( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] ) {
734 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
735 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
737 else if ( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] ) {
741 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
742 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
749 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
750 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
753 /* check for bogus axis */
754 if ( faxis[ axisNum ] == 0.0f ) {
755 Sys_FPrintf( SYS_WRN, "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
760 /* store the axis number in the lightmap */
761 lm->axisNum = axisNum;
763 /* walk the list of surfaces on this raw lightmap */
764 for ( n = 0; n < lm->numLightSurfaces; n++ )
767 num2 = lightSurfaces[ lm->firstLightSurface + n ];
768 ds2 = &bspDrawSurfaces[ num2 ];
769 verts = &yDrawVerts[ ds2->firstVert ];
771 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
772 for ( i = 0; i < ds2->numVerts; i++ )
774 VectorSubtract( verts[ i ].xyz, origin, delta );
775 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
776 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
777 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
778 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
780 if ( s > (float) lm->w || t > (float) lm->h ) {
781 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
782 s, lm->w, t, lm->h );
787 /* get first drawsurface */
788 num2 = lightSurfaces[ lm->firstLightSurface ];
789 ds2 = &bspDrawSurfaces[ num2 ];
790 verts = &yDrawVerts[ ds2->firstVert ];
792 /* calculate lightmap origin */
793 if ( VectorLength( ds2->lightmapVecs[ 2 ] ) ) {
794 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
797 VectorCopy( lm->axis, plane );
799 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
801 VectorCopy( origin, lm->origin );
802 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
803 d /= plane[ axisNum ];
804 lm->origin[ axisNum ] -= d;
807 VectorCopy( lm->origin, ds->lightmapOrigin );
809 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
810 if ( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) { /* ydnar: can't remember what exactly i was thinking here... */
811 /* allocate space for the vectors */
812 lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
813 memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
814 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
816 /* project stepped lightmap blocks and subtract to get planevecs */
817 for ( i = 0; i < 2; i++ )
819 len = VectorNormalize( vecs[ i ], normalized );
820 VectorScale( normalized, ( 1.0 / len ), lm->vecs[ i ] );
821 d = DotProduct( lm->vecs[ i ], plane );
822 d /= plane[ axisNum ];
823 lm->vecs[ i ][ axisNum ] -= d;
828 /* lightmap vectors are useless on a non-planar surface */
833 if ( ds->surfaceType == MST_PATCH ) {
834 numPatchesLightmapped++;
835 if ( lm->plane != NULL ) {
836 numPlanarPatchesLightmapped++;
841 if ( lm->plane != NULL ) {
842 numPlanarsLightmapped++;
845 numNonPlanarsLightmapped++;
857 compare function for qsort()
860 static int CompareSurfaceInfo( const void *a, const void *b ){
861 surfaceInfo_t *aInfo, *bInfo;
865 /* get surface info */
866 aInfo = &surfaceInfos[ *( (const int*) a ) ];
867 bInfo = &surfaceInfos[ *( (const int*) b ) ];
870 if ( aInfo->modelindex < bInfo->modelindex ) {
873 else if ( aInfo->modelindex > bInfo->modelindex ) {
877 /* then lightmap status */
878 if ( aInfo->hasLightmap < bInfo->hasLightmap ) {
881 else if ( aInfo->hasLightmap > bInfo->hasLightmap ) {
885 /* 27: then shader! */
886 if ( aInfo->si < bInfo->si ) {
889 else if ( aInfo->si > bInfo->si ) {
893 /* then lightmap sample size */
894 if ( aInfo->sampleSize < bInfo->sampleSize ) {
897 else if ( aInfo->sampleSize > bInfo->sampleSize ) {
901 /* then lightmap axis */
902 for ( i = 0; i < 3; i++ )
904 if ( aInfo->axis[ i ] < bInfo->axis[ i ] ) {
907 else if ( aInfo->axis[ i ] > bInfo->axis[ i ] ) {
913 if ( aInfo->plane == NULL && bInfo->plane != NULL ) {
916 else if ( aInfo->plane != NULL && bInfo->plane == NULL ) {
919 else if ( aInfo->plane != NULL && bInfo->plane != NULL ) {
920 for ( i = 0; i < 4; i++ )
922 if ( aInfo->plane[ i ] < bInfo->plane[ i ] ) {
925 else if ( aInfo->plane[ i ] > bInfo->plane[ i ] ) {
931 /* then position in world */
932 for ( i = 0; i < 3; i++ )
934 if ( aInfo->mins[ i ] < bInfo->mins[ i ] ) {
937 else if ( aInfo->mins[ i ] > bInfo->mins[ i ] ) {
942 /* these are functionally identical (this should almost never happen) */
949 SetupSurfaceLightmaps()
950 allocates lightmaps for every surface in the bsp that needs one
951 this depends on yDrawVerts being allocated
954 void SetupSurfaceLightmaps( void ){
955 int i, j, k, s,num, num2;
958 bspDrawSurface_t *ds;
959 surfaceInfo_t *info, *info2;
962 vec3_t mapSize, entityOrigin;
966 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n" );
968 /* determine supersample amount */
969 if ( superSample < 1 ) {
972 else if ( superSample > 8 ) {
973 Sys_FPrintf( SYS_WRN, "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
977 /* clear map bounds */
978 ClearBounds( mapMins, mapMaxs );
980 /* allocate a list of surface clusters */
981 numSurfaceClusters = 0;
982 maxSurfaceClusters = numBSPLeafSurfaces;
983 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
984 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
986 /* allocate a list for per-surface info */
987 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
988 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
989 for ( i = 0; i < numBSPDrawSurfaces; i++ )
990 surfaceInfos[ i ].childSurfaceNum = -1;
992 /* allocate a list of surface indexes to be sorted */
993 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
994 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
996 /* walk each model in the bsp */
997 for ( i = 0; i < numBSPModels; i++ )
1000 model = &bspModels[ i ];
1002 /* walk the list of surfaces in this model and fill out the info structs */
1003 for ( j = 0; j < model->numBSPSurfaces; j++ )
1005 /* make surface index */
1006 num = model->firstBSPSurface + j;
1008 /* copy index to sort list */
1009 sortSurfaces[ num ] = num;
1011 /* get surface and info */
1012 ds = &bspDrawSurfaces[ num ];
1013 info = &surfaceInfos[ num ];
1015 /* set entity origin */
1016 if ( ds->numVerts > 0 ) {
1017 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
1020 VectorClear( entityOrigin );
1024 info->modelindex = i;
1027 info->firstSurfaceCluster = numSurfaceClusters;
1029 /* get extra data */
1030 info->si = GetSurfaceExtraShaderInfo( num );
1031 if ( info->si == NULL ) {
1032 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
1034 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
1035 info->entityNum = GetSurfaceExtraEntityNum( num );
1036 info->castShadows = GetSurfaceExtraCastShadows( num );
1037 info->recvShadows = GetSurfaceExtraRecvShadows( num );
1038 info->sampleSize = GetSurfaceExtraSampleSize( num );
1039 info->longestCurve = GetSurfaceExtraLongestCurve( num );
1040 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
1041 GetSurfaceExtraLightmapAxis( num, info->axis );
1044 if ( info->parentSurfaceNum >= 0 ) {
1045 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1048 /* determine surface bounds */
1049 ClearBounds( info->mins, info->maxs );
1050 for ( k = 0; k < ds->numVerts; k++ )
1052 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1053 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1056 /* find all the bsp clusters the surface falls into */
1057 for ( k = 0; k < numBSPLeafs; k++ )
1060 leaf = &bspLeafs[ k ];
1063 if ( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1064 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1065 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] ) {
1069 /* test leaf surfaces */
1070 for ( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1072 if ( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num ) {
1073 if ( numSurfaceClusters >= maxSurfaceClusters ) {
1074 Error( "maxSurfaceClusters exceeded" );
1076 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1077 numSurfaceClusters++;
1078 info->numSurfaceClusters++;
1083 /* determine if surface is planar */
1084 if ( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f ) {
1086 info->plane = safe_malloc( 4 * sizeof( float ) );
1087 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1088 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1091 /* determine if surface requires a lightmap */
1092 if ( ds->surfaceType == MST_TRIANGLE_SOUP ||
1093 ds->surfaceType == MST_FOLIAGE ||
1094 ( info->si->compileFlags & C_VERTEXLIT ) ) {
1095 numSurfsVertexLit++;
1099 numSurfsLightmapped++;
1100 info->hasLightmap = qtrue;
1105 /* find longest map distance */
1106 VectorSubtract( mapMaxs, mapMins, mapSize );
1107 maxMapDistance = VectorLength( mapSize );
1109 /* sort the surfaces info list */
1110 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1112 /* allocate a list of surfaces that would go into raw lightmaps */
1113 numLightSurfaces = 0;
1114 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1115 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1117 /* allocate a list of raw lightmaps */
1118 numRawSuperLuxels = 0;
1119 numRawLightmaps = 0;
1120 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1121 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1123 /* walk the list of sorted surfaces */
1124 for ( i = 0; i < numBSPDrawSurfaces; i++ )
1126 /* get info and attempt early out */
1127 num = sortSurfaces[ i ];
1128 ds = &bspDrawSurfaces[ num ];
1129 info = &surfaceInfos[ num ];
1130 if ( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 ) {
1134 /* allocate a new raw lightmap */
1135 lm = &rawLightmaps[ numRawLightmaps ];
1139 lm->splotchFix = info->si->splotchFix;
1140 lm->firstLightSurface = numLightSurfaces;
1141 lm->numLightSurfaces = 0;
1142 /* vortex: multiply lightmap sample size by -samplescale */
1143 if ( sampleScale > 0 ) {
1144 lm->sampleSize = info->sampleSize * sampleScale;
1147 lm->sampleSize = info->sampleSize;
1149 lm->actualSampleSize = lm->sampleSize;
1150 lm->entityNum = info->entityNum;
1151 lm->recvShadows = info->recvShadows;
1152 lm->brightness = info->si->lmBrightness;
1153 lm->filterRadius = info->si->lmFilterRadius;
1154 VectorCopy( info->si->floodlightRGB, lm->floodlightRGB );
1155 lm->floodlightDistance = info->si->floodlightDistance;
1156 lm->floodlightIntensity = info->si->floodlightIntensity;
1157 lm->floodlightDirectionScale = info->si->floodlightDirectionScale;
1158 VectorCopy( info->axis, lm->axis );
1159 lm->plane = info->plane;
1160 VectorCopy( info->mins, lm->mins );
1161 VectorCopy( info->maxs, lm->maxs );
1163 lm->customWidth = info->si->lmCustomWidth;
1164 lm->customHeight = info->si->lmCustomHeight;
1166 /* add the surface to the raw lightmap */
1167 AddSurfaceToRawLightmap( num, lm );
1170 /* do an exhaustive merge */
1174 /* walk the list of surfaces again */
1176 for ( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1178 /* get info and attempt early out */
1179 num2 = sortSurfaces[ j ];
1180 info2 = &surfaceInfos[ num2 ];
1181 if ( info2->hasLightmap == qfalse || info2->lm != NULL ) {
1185 /* add the surface to the raw lightmap */
1186 if ( AddSurfaceToRawLightmap( num2, lm ) ) {
1193 lm->numLightSurfaces--;
1199 /* finish the lightmap and allocate the various buffers */
1200 FinishRawLightmap( lm );
1203 /* allocate vertex luxel storage */
1204 for ( k = 0; k < MAX_LIGHTMAPS; k++ )
1206 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1207 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1208 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1209 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1212 /* emit some stats */
1213 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1214 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1215 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1216 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1217 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1218 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1219 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1220 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1226 StitchSurfaceLightmaps()
1227 stitches lightmap edges
1228 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1231 #define MAX_STITCH_CANDIDATES 32
1232 #define MAX_STITCH_LUXELS 64
1234 void StitchSurfaceLightmaps( void ){
1235 int i, j, x, y, x2, y2, *cluster, *cluster2,
1236 numStitched, numCandidates, numLuxels, f, fOld, start;
1237 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1238 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1239 sampleSize, average[ 3 ], totalColor, ootc;
1242 /* disabled for now */
1246 Sys_Printf( "--- StitchSurfaceLightmaps ---\n" );
1250 start = I_FloatTime();
1252 /* walk the list of raw lightmaps */
1254 for ( i = 0; i < numRawLightmaps; i++ )
1256 /* print pacifier */
1257 f = 10 * i / numRawLightmaps;
1260 Sys_Printf( "%i...", f );
1263 /* get lightmap a */
1264 a = &rawLightmaps[ i ];
1266 /* walk rest of lightmaps */
1268 for ( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1270 /* get lightmap b */
1271 b = &rawLightmaps[ j ];
1273 /* test bounding box */
1274 if ( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1275 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1276 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] ) {
1281 c[ numCandidates++ ] = b;
1285 for ( y = 0; y < a->sh; y++ )
1287 for ( x = 0; x < a->sw; x++ )
1289 /* ignore unmapped/unlit luxels */
1291 cluster = SUPER_CLUSTER( x, y );
1292 if ( *cluster == CLUSTER_UNMAPPED ) {
1295 luxel = SUPER_LUXEL( 0, x, y );
1296 if ( luxel[ 3 ] <= 0.0f ) {
1300 /* get particulars */
1301 origin = SUPER_ORIGIN( x, y );
1302 normal = SUPER_NORMAL( x, y );
1304 /* walk candidate list */
1305 for ( j = 0; j < numCandidates; j++ )
1311 /* set samplesize to the smaller of the pair */
1312 sampleSize = 0.5f * ( a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize );
1314 /* test bounding box */
1315 if ( origin[ 0 ] < ( b->mins[ 0 ] - sampleSize ) || ( origin[ 0 ] > b->maxs[ 0 ] + sampleSize ) ||
1316 origin[ 1 ] < ( b->mins[ 1 ] - sampleSize ) || ( origin[ 1 ] > b->maxs[ 1 ] + sampleSize ) ||
1317 origin[ 2 ] < ( b->mins[ 2 ] - sampleSize ) || ( origin[ 2 ] > b->maxs[ 2 ] + sampleSize ) ) {
1321 /* walk candidate luxels */
1322 VectorClear( average );
1325 for ( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1327 for ( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1329 /* ignore same luxels */
1330 if ( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 ) {
1334 /* ignore unmapped/unlit luxels */
1335 cluster2 = SUPER_CLUSTER( x2, y2 );
1336 if ( *cluster2 == CLUSTER_UNMAPPED ) {
1339 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1340 if ( luxel2[ 3 ] <= 0.0f ) {
1344 /* get particulars */
1345 origin2 = SUPER_ORIGIN( x2, y2 );
1346 normal2 = SUPER_NORMAL( x2, y2 );
1349 if ( DotProduct( normal, normal2 ) < 0.5f ) {
1354 if ( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1355 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1356 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize ) {
1361 //% VectorSet( luxel2, 255, 0, 255 );
1362 VectorAdd( average, luxel2, average );
1363 totalColor += luxel2[ 3 ];
1368 if ( numLuxels == 0 ) {
1373 ootc = 1.0f / totalColor;
1374 VectorScale( average, ootc, luxel );
1382 /* emit statistics */
1383 Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
1384 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1391 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1394 #define SOLID_EPSILON 0.0625
1395 #define LUXEL_TOLERANCE 0.0025
1396 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1398 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1401 double delta, total, rd, gd, bd;
1402 float *aLuxel, *bLuxel;
1405 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1406 if ( ( minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ] ) &&
1407 ( ( aNum == 0 && bNum != 0 ) || ( aNum != 0 && bNum == 0 ) ) ) {
1412 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1413 a->brightness != b->brightness ||
1414 a->solid[ aNum ] != b->solid[ bNum ] ||
1415 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1419 /* compare solid color lightmaps */
1420 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1422 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1423 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1424 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1427 if ( rd > SOLID_EPSILON || gd > SOLID_EPSILON || bd > SOLID_EPSILON ) {
1435 /* compare nonsolid lightmaps */
1436 if ( a->w != b->w || a->h != b->h ) {
1440 /* compare luxels */
1443 for ( y = 0; y < a->h; y++ )
1445 for ( x = 0; x < a->w; x++ )
1447 /* increment total */
1451 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1452 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1454 /* ignore unused luxels */
1455 if ( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 ) {
1460 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1461 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1462 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1464 /* 2003-09-27: compare individual luxels */
1465 if ( rd > 3.0 || gd > 3.0 || bd > 3.0 ) {
1469 /* compare (fixme: take into account perceptual differences) */
1470 delta += rd * LUXEL_COLOR_FRAC;
1471 delta += gd * LUXEL_COLOR_FRAC;
1472 delta += bd * LUXEL_COLOR_FRAC;
1474 /* is the change too high? */
1475 if ( total > 0.0 && ( ( delta / total ) > LUXEL_TOLERANCE ) ) {
1481 /* made it this far, they must be identical (or close enough) */
1489 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1492 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum ){
1495 float luxel[ 3 ], *aLuxel, *bLuxel;
1499 if ( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1500 a->brightness != b->brightness ||
1501 a->solid[ aNum ] != b->solid[ bNum ] ||
1502 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL ) {
1506 /* compare solid lightmaps */
1507 if ( a->solid[ aNum ] && b->solid[ bNum ] ) {
1509 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1510 VectorScale( luxel, 0.5f, luxel );
1513 VectorCopy( luxel, a->solidColor[ aNum ] );
1514 VectorCopy( luxel, b->solidColor[ bNum ] );
1516 /* return to sender */
1520 /* compare nonsolid lightmaps */
1521 if ( a->w != b->w || a->h != b->h ) {
1526 for ( y = 0; y < a->h; y++ )
1528 for ( x = 0; x < a->w; x++ )
1531 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1532 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1534 /* handle occlusion mismatch */
1535 if ( aLuxel[ 0 ] < 0.0f ) {
1536 VectorCopy( bLuxel, aLuxel );
1538 else if ( bLuxel[ 0 ] < 0.0f ) {
1539 VectorCopy( aLuxel, bLuxel );
1544 VectorAdd( aLuxel, bLuxel, luxel );
1545 VectorScale( luxel, 0.5f, luxel );
1547 /* debugging code */
1548 //% luxel[ 2 ] += 64.0f;
1551 VectorCopy( luxel, aLuxel );
1552 VectorCopy( luxel, bLuxel );
1565 determines if a single luxel is can be approximated with the interpolated vertex rgba
1568 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv ){
1569 int i, x, y, d, lightmapNum;
1571 vec3_t color, vertexColor;
1572 byte cb[ 4 ], vcb[ 4 ];
1575 /* find luxel xy coords */
1576 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1577 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1581 else if ( x >= lm->w ) {
1587 else if ( y >= lm->h ) {
1592 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1595 if ( lm->styles[ lightmapNum ] == LS_NONE ) {
1600 luxel = BSP_LUXEL( lightmapNum, x, y );
1602 /* ignore occluded luxels */
1603 if ( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f ) {
1607 /* copy, set min color and compare */
1608 VectorCopy( luxel, color );
1609 VectorCopy( dv->color[ 0 ], vertexColor );
1611 /* styles are not affected by minlight */
1612 if ( lightmapNum == 0 ) {
1613 for ( i = 0; i < 3; i++ )
1616 if ( color[ i ] < minLight[ i ] ) {
1617 color[ i ] = minLight[ i ];
1619 if ( vertexColor[ i ] < minLight[ i ] ) { /* note NOT minVertexLight */
1620 vertexColor[ i ] = minLight[ i ];
1626 ColorToBytes( color, cb, 1.0f );
1627 ColorToBytes( vertexColor, vcb, 1.0f );
1630 for ( i = 0; i < 3; i++ )
1632 d = cb[ i ] - vcb[ i ];
1636 if ( d > approximateTolerance ) {
1642 /* close enough for the girls i date */
1649 ApproximateTriangle()
1650 determines if a single triangle can be approximated with vertex rgba
1653 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] ){
1654 bspDrawVert_t mid, *dv2[ 3 ];
1658 /* approximate the vertexes */
1659 if ( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse ) {
1662 if ( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse ) {
1665 if ( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse ) {
1669 /* subdivide calc */
1672 float dx, dy, dist, maxDist;
1675 /* find the longest edge and split it */
1678 for ( i = 0; i < 3; i++ )
1680 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 0 ];
1681 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ][ 1 ];
1682 dist = sqrt( ( dx * dx ) + ( dy * dy ) );
1683 if ( dist > maxDist ) {
1689 /* try to early out */
1690 if ( i < 0 || maxDist < subdivideThreshold ) {
1695 /* split the longest edge and map it */
1696 LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
1697 if ( ApproximateLuxel( lm, &mid ) == qfalse ) {
1701 /* recurse to first triangle */
1702 VectorCopy( dv, dv2 );
1704 if ( ApproximateTriangle_r( lm, dv2 ) == qfalse ) {
1708 /* recurse to second triangle */
1709 VectorCopy( dv, dv2 );
1710 dv2[ ( max + 1 ) % 3 ] = ∣
1711 return ApproximateTriangle_r( lm, dv2 );
1717 ApproximateLightmap()
1718 determines if a raw lightmap can be approximated sufficiently with vertex colors
1721 static qboolean ApproximateLightmap( rawLightmap_t *lm ){
1722 int n, num, i, x, y, pw[ 5 ], r;
1723 bspDrawSurface_t *ds;
1724 surfaceInfo_t *info;
1725 mesh_t src, *subdivided, *mesh;
1726 bspDrawVert_t *verts, *dv[ 3 ];
1727 qboolean approximated;
1730 /* approximating? */
1731 if ( approximateTolerance <= 0 ) {
1735 /* test for jmonroe */
1737 /* don't approx lightmaps with styled twins */
1738 if ( lm->numStyledTwins > 0 ) {
1742 /* don't approx lightmaps with styles */
1743 for ( i = 1; i < MAX_LIGHTMAPS; i++ )
1745 if ( lm->styles[ i ] != LS_NONE ) {
1751 /* assume reduced until shadow detail is found */
1752 approximated = qtrue;
1754 /* walk the list of surfaces on this raw lightmap */
1755 for ( n = 0; n < lm->numLightSurfaces; n++ )
1758 num = lightSurfaces[ lm->firstLightSurface + n ];
1759 ds = &bspDrawSurfaces[ num ];
1760 info = &surfaceInfos[ num ];
1762 /* assume not-reduced initially */
1763 info->approximated = qfalse;
1765 /* bail if lightmap doesn't match up */
1766 if ( info->lm != lm ) {
1770 /* bail if not vertex lit */
1771 if ( info->si->noVertexLight ) {
1775 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1776 if ( ( info->maxs[ 0 ] - info->mins[ 0 ] ) <= ( 2.0f * info->sampleSize ) &&
1777 ( info->maxs[ 1 ] - info->mins[ 1 ] ) <= ( 2.0f * info->sampleSize ) &&
1778 ( info->maxs[ 2 ] - info->mins[ 2 ] ) <= ( 2.0f * info->sampleSize ) ) {
1779 info->approximated = qtrue;
1780 numSurfsVertexForced++;
1784 /* handle the triangles */
1785 switch ( ds->surfaceType )
1789 verts = yDrawVerts + ds->firstVert;
1791 /* map the triangles */
1792 info->approximated = qtrue;
1793 for ( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1795 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1796 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1797 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1798 info->approximated = ApproximateTriangle_r( lm, dv );
1803 /* make a mesh from the drawsurf */
1804 src.width = ds->patchWidth;
1805 src.height = ds->patchHeight;
1806 src.verts = &yDrawVerts[ ds->firstVert ];
1807 //% subdivided = SubdivideMesh( src, 8, 512 );
1808 subdivided = SubdivideMesh2( src, info->patchIterations );
1810 /* fit it to the curve and remove colinear verts on rows/columns */
1811 PutMeshOnCurve( *subdivided );
1812 mesh = RemoveLinearMeshColumnsRows( subdivided );
1813 FreeMesh( subdivided );
1816 verts = mesh->verts;
1818 /* map the mesh quads */
1819 info->approximated = qtrue;
1820 for ( y = 0; y < ( mesh->height - 1 ) && info->approximated; y++ )
1822 for ( x = 0; x < ( mesh->width - 1 ) && info->approximated; x++ )
1825 pw[ 0 ] = x + ( y * mesh->width );
1826 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1827 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1828 pw[ 3 ] = x + 1 + ( y * mesh->width );
1829 pw[ 4 ] = x + ( y * mesh->width ); /* same as pw[ 0 ] */
1834 /* get drawverts and map first triangle */
1835 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1836 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1837 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1838 info->approximated = ApproximateTriangle_r( lm, dv );
1840 /* get drawverts and map second triangle */
1841 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1842 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1843 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1844 if ( info->approximated ) {
1845 info->approximated = ApproximateTriangle_r( lm, dv );
1859 if ( info->approximated == qfalse ) {
1860 approximated = qfalse;
1863 numSurfsVertexApproximated++;
1868 return approximated;
1874 TestOutLightmapStamp()
1875 tests a stamp on a given lightmap for validity
1878 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y ){
1879 int sx, sy, ox, oy, offset;
1884 if ( x < 0 || y < 0 || ( x + lm->w ) > olm->customWidth || ( y + lm->h ) > olm->customHeight ) {
1888 /* solid lightmaps test a 1x1 stamp */
1889 if ( lm->solid[ lightmapNum ] ) {
1890 offset = ( y * olm->customWidth ) + x;
1891 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1897 /* test the stamp */
1898 for ( sy = 0; sy < lm->h; sy++ )
1900 for ( sx = 0; sx < lm->w; sx++ )
1903 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1904 if ( luxel[ 0 ] < 0.0f ) {
1908 /* get bsp lightmap coords and test */
1911 offset = ( oy * olm->customWidth ) + ox;
1912 if ( olm->lightBits[ offset >> 3 ] & ( 1 << ( offset & 7 ) ) ) {
1918 /* stamp is empty */
1926 sets up an output lightmap
1929 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm ){
1931 if ( lm == NULL || olm == NULL ) {
1935 /* is this a "normal" bsp-stored lightmap? */
1936 if ( ( lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize ) || externalLightmaps ) {
1937 olm->lightmapNum = numBSPLightmaps;
1940 /* lightmaps are interleaved with light direction maps */
1946 olm->lightmapNum = -3;
1949 /* set external lightmap number */
1950 olm->extLightmapNum = -1;
1953 olm->numLightmaps = 0;
1954 olm->customWidth = lm->customWidth;
1955 olm->customHeight = lm->customHeight;
1956 olm->freeLuxels = olm->customWidth * olm->customHeight;
1957 olm->numShaders = 0;
1959 /* allocate buffers */
1960 olm->lightBits = safe_malloc( ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1961 memset( olm->lightBits, 0, ( olm->customWidth * olm->customHeight / 8 ) + 8 );
1962 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1963 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1965 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1966 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1974 for a given surface lightmap, find output lightmap pages and positions for it
1977 #define LIGHTMAP_RESERVE_COUNT 1
1978 static void FindOutLightmaps( rawLightmap_t *lm, qboolean fastAllocate ){
1979 int i, j, k, lightmapNum, xMax, yMax, x = -1, y = -1, sx, sy, ox, oy, offset;
1981 surfaceInfo_t *info;
1982 float *luxel, *deluxel;
1983 vec3_t color, direction;
1986 int xIncrement, yIncrement;
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 /* if fast allocation, skip lightmap files that are more than 90% complete */
2098 if ( fastAllocate == qtrue ) {
2099 if (olm->freeLuxels < (olm->customWidth * olm->customHeight) / 10) {
2104 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
2105 if ( olm->customWidth != lm->customWidth ||
2106 olm->customHeight != lm->customHeight ) {
2111 if ( lm->solid[ lightmapNum ] ) {
2112 xMax = olm->customWidth;
2113 yMax = olm->customHeight;
2117 xMax = ( olm->customWidth - lm->w ) + 1;
2118 yMax = ( olm->customHeight - lm->h ) + 1;
2121 /* if fast allocation, do not test allocation on every pixels, especially for large lightmaps */
2122 if ( fastAllocate == qtrue ) {
2123 xIncrement = MAX(1, lm->w / 15);
2124 yIncrement = MAX(1, lm->h / 15);
2131 /* walk the origin around the lightmap */
2132 for ( y = 0; y < yMax; y += yIncrement )
2134 for ( x = 0; x < xMax; x += xIncrement )
2136 /* find a fine tract of lauhnd */
2137 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2160 if ( ok == qfalse ) {
2161 /* allocate LIGHTMAP_RESERVE_COUNT new output lightmaps */
2162 numOutLightmaps += LIGHTMAP_RESERVE_COUNT;
2163 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2164 if ( outLightmaps != NULL && numOutLightmaps > LIGHTMAP_RESERVE_COUNT ) {
2165 memcpy( olm, outLightmaps, ( numOutLightmaps - LIGHTMAP_RESERVE_COUNT ) * sizeof( outLightmap_t ) );
2166 free( outLightmaps );
2170 /* initialize both out lightmaps */
2171 for ( k = numOutLightmaps - LIGHTMAP_RESERVE_COUNT; k < numOutLightmaps; ++k )
2172 SetupOutLightmap( lm, &outLightmaps[ k ] );
2174 /* set out lightmap */
2175 i = numOutLightmaps - LIGHTMAP_RESERVE_COUNT;
2176 olm = &outLightmaps[ i ];
2178 /* set stamp xy origin to the first surface lightmap */
2179 if ( lightmapNum > 0 ) {
2180 x = lm->lightmapX[ 0 ];
2181 y = lm->lightmapY[ 0 ];
2185 /* if this is a style-using lightmap, it must be exported */
2186 if ( lightmapNum > 0 && game->load != LoadRBSPFile ) {
2187 olm->extLightmapNum = 0;
2190 /* add the surface lightmap to the bsp lightmap */
2191 lm->outLightmapNums[ lightmapNum ] = i;
2192 lm->lightmapX[ lightmapNum ] = x;
2193 lm->lightmapY[ lightmapNum ] = y;
2194 olm->numLightmaps++;
2197 for ( i = 0; i < lm->numLightSurfaces; i++ )
2199 /* get surface info */
2200 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2202 /* test for shader */
2203 for ( j = 0; j < olm->numShaders; j++ )
2205 if ( olm->shaders[ j ] == info->si ) {
2210 /* if it doesn't exist, add it */
2211 if ( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS ) {
2212 olm->shaders[ olm->numShaders ] = info->si;
2214 numLightmapShaders++;
2219 if ( lm->solid[ lightmapNum ] ) {
2229 /* mark the bits used */
2230 for ( y = 0; y < yMax; y++ )
2232 for ( x = 0; x < xMax; x++ )
2235 luxel = BSP_LUXEL( lightmapNum, x, y );
2236 deluxel = BSP_DELUXEL( x, y );
2237 if ( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ] ) {
2241 /* set minimum light */
2242 if ( lm->solid[ lightmapNum ] ) {
2244 VectorSet( color, 255.0f, 0.0f, 0.0f );
2247 VectorCopy( lm->solidColor[ lightmapNum ], color );
2251 VectorCopy( luxel, color );
2254 /* styles are not affected by minlight */
2255 if ( lightmapNum == 0 ) {
2256 for ( i = 0; i < 3; i++ )
2258 if ( color[ i ] < minLight[ i ] ) {
2259 color[ i ] = minLight[ i ];
2264 /* get bsp lightmap coords */
2265 ox = x + lm->lightmapX[ lightmapNum ];
2266 oy = y + lm->lightmapY[ lightmapNum ];
2267 offset = ( oy * olm->customWidth ) + ox;
2269 /* flag pixel as used */
2270 olm->lightBits[ offset >> 3 ] |= ( 1 << ( offset & 7 ) );
2274 pixel = olm->bspLightBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2275 ColorToBytes( color, pixel, lm->brightness );
2277 /* store direction */
2279 /* normalize average light direction */
2280 pixel = olm->bspDirBytes + ( ( ( oy * olm->customWidth ) + ox ) * 3 );
2281 VectorScale( deluxel, 1000.0f, direction );
2282 VectorNormalize( direction, direction );
2283 VectorScale( direction, 127.5f, direction );
2284 for ( i = 0; i < 3; i++ )
2285 pixel[ i ] = (byte)( 127.5f + direction[ i ] );
2295 CompareRawLightmap()
2296 compare function for qsort()
2299 static int CompareRawLightmap( const void *a, const void *b ){
2300 rawLightmap_t *alm, *blm;
2301 surfaceInfo_t *aInfo, *bInfo;
2306 alm = &rawLightmaps[ *( (const int*) a ) ];
2307 blm = &rawLightmaps[ *( (const int*) b ) ];
2309 /* get min number of surfaces */
2310 min = ( alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces );
2313 for ( i = 0; i < min; i++ )
2315 /* get surface info */
2316 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2317 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2319 /* compare shader names */
2320 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2326 /* test style count */
2328 for ( i = 0; i < MAX_LIGHTMAPS; i++ )
2329 diff += blm->styles[ i ] - alm->styles[ i ];
2335 diff = ( blm->w * blm->h ) - ( alm->w * alm->h );
2340 /* must be equivalent */
2346 void FillOutLightmap( outLightmap_t *olm ){
2349 vec3_t dir_sum, light_sum;
2351 byte *lightBitsNew = NULL;
2352 byte *lightBytesNew = NULL;
2353 byte *dirBytesNew = NULL;
2355 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2356 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2358 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2362 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2363 olm->lightBits[0] |= 1;
2364 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2365 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2366 olm->bspLightBytes[0] = 255;
2367 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2370 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2371 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2373 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2379 for ( y = 0; y < olm->customHeight; ++y )
2381 for ( x = 0; x < olm->customWidth; ++x )
2383 ofs = y * olm->customWidth + x;
2384 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2388 VectorClear( dir_sum );
2389 VectorClear( light_sum );
2391 /* try all four neighbors */
2392 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2393 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2395 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2397 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2401 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2402 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2404 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2406 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2410 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2411 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2413 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2415 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2419 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2420 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2422 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2424 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2430 ofs = y * olm->customWidth + x;
2431 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2432 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2434 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2444 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2445 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2447 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2451 free( lightBitsNew );
2452 free( lightBytesNew );
2454 free( dirBytesNew );
2461 StoreSurfaceLightmaps()
2462 stores the surface lightmaps into the bsp as byte rgb triplets
2465 void StoreSurfaceLightmaps( qboolean fastAllocate ){
2466 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2467 int style, size, lightmapNum, lightmapNum2;
2468 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2469 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2470 float *deluxel, *bspDeluxel, *bspDeluxel2;
2472 int numUsed, numTwins, numTwinLuxels, numStored;
2473 float lmx, lmy, efficiency;
2475 bspDrawSurface_t *ds, *parent, dsTemp;
2476 surfaceInfo_t *info;
2477 rawLightmap_t *lm, *lm2;
2479 bspDrawVert_t *dv, *ydv, *dvParent;
2480 char dirname[ 1024 ], filename[ 1024 ];
2482 char lightmapName[ 128 ];
2483 const char *rgbGenValues[ 256 ];
2484 const char *alphaGenValues[ 256 ];
2488 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2491 if ( lmCustomDir ) {
2492 strcpy( dirname, lmCustomDir );
2496 strcpy( dirname, source );
2497 StripExtension( dirname );
2499 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2500 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2502 /* -----------------------------------------------------------------
2503 average the sampled luxels into the bsp luxels
2504 ----------------------------------------------------------------- */
2507 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2509 /* walk the list of raw lightmaps */
2513 numSolidLightmaps = 0;
2514 for ( i = 0; i < numRawLightmaps; i++ )
2517 lm = &rawLightmaps[ i ];
2519 /* walk individual lightmaps */
2520 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2523 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2527 /* allocate bsp luxel storage */
2528 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2529 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2530 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2531 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2534 /* allocate radiosity lightmap storage */
2536 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2537 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2538 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2540 memset( lm->radLuxels[ lightmapNum ], 0, size );
2543 /* average supersampled luxels */
2544 for ( y = 0; y < lm->h; y++ )
2546 for ( x = 0; x < lm->w; x++ )
2550 occludedSamples = 0.0f;
2552 VectorClear( sample );
2553 VectorClear( occludedSample );
2554 VectorClear( dirSample );
2555 for ( ly = 0; ly < superSample; ly++ )
2557 for ( lx = 0; lx < superSample; lx++ )
2560 sx = x * superSample + lx;
2561 sy = y * superSample + ly;
2562 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2563 deluxel = SUPER_DELUXEL( sx, sy );
2564 normal = SUPER_NORMAL( sx, sy );
2565 cluster = SUPER_CLUSTER( sx, sy );
2567 /* sample deluxemap */
2568 if ( deluxemap && lightmapNum == 0 ) {
2569 VectorAdd( dirSample, deluxel, dirSample );
2572 /* keep track of used/occluded samples */
2573 if ( *cluster != CLUSTER_UNMAPPED ) {
2577 /* handle lightmap border? */
2578 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2579 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2584 else if ( debug && *cluster < 0 ) {
2585 if ( *cluster == CLUSTER_UNMAPPED ) {
2586 VectorSet( luxel, 255, 204, 0 );
2588 else if ( *cluster == CLUSTER_OCCLUDED ) {
2589 VectorSet( luxel, 255, 0, 255 );
2591 else if ( *cluster == CLUSTER_FLOODED ) {
2592 VectorSet( luxel, 0, 32, 255 );
2594 VectorAdd( occludedSample, luxel, occludedSample );
2595 occludedSamples += 1.0f;
2598 /* normal luxel handling */
2599 else if ( luxel[ 3 ] > 0.0f ) {
2600 /* handle lit or flooded luxels */
2601 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2602 VectorAdd( sample, luxel, sample );
2603 samples += luxel[ 3 ];
2606 /* handle occluded or unmapped luxels */
2609 VectorAdd( occludedSample, luxel, occludedSample );
2610 occludedSamples += luxel[ 3 ];
2613 /* handle style debugging */
2614 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2615 VectorCopy( debugColors[ 0 ], sample );
2622 /* only use occluded samples if necessary */
2623 if ( samples <= 0.0f ) {
2624 VectorCopy( occludedSample, sample );
2625 samples = occludedSamples;
2629 luxel = SUPER_LUXEL( lightmapNum, x, y );
2630 deluxel = SUPER_DELUXEL( x, y );
2632 /* store light direction */
2633 if ( deluxemap && lightmapNum == 0 ) {
2634 VectorCopy( dirSample, deluxel );
2637 /* store the sample back in super luxels */
2638 if ( samples > 0.01f ) {
2639 VectorScale( sample, ( 1.0f / samples ), luxel );
2643 /* if any samples were mapped in any way, store ambient color */
2644 else if ( mappedSamples > 0 ) {
2645 if ( lightmapNum == 0 ) {
2646 VectorCopy( ambientColor, luxel );
2649 VectorClear( luxel );
2654 /* store a bogus value to be fixed later */
2657 VectorClear( luxel );
2665 ClearBounds( colorMins, colorMaxs );
2667 /* clean up and store into bsp luxels */
2668 for ( y = 0; y < lm->h; y++ )
2670 for ( x = 0; x < lm->w; x++ )
2673 luxel = SUPER_LUXEL( lightmapNum, x, y );
2674 deluxel = SUPER_DELUXEL( x, y );
2676 /* copy light direction */
2677 if ( deluxemap && lightmapNum == 0 ) {
2678 VectorCopy( deluxel, dirSample );
2681 /* is this a valid sample? */
2682 if ( luxel[ 3 ] > 0.0f ) {
2683 VectorCopy( luxel, sample );
2684 samples = luxel[ 3 ];
2688 /* fix negative samples */
2689 for ( j = 0; j < 3; j++ )
2691 if ( sample[ j ] < 0.0f ) {
2698 /* nick an average value from the neighbors */
2699 VectorClear( sample );
2700 VectorClear( dirSample );
2703 /* fixme: why is this disabled?? */
2704 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2706 if ( sy < 0 || sy >= lm->h ) {
2710 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2712 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2716 /* get neighbor's particulars */
2717 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2718 if ( luxel[ 3 ] < 0.0f ) {
2721 VectorAdd( sample, luxel, sample );
2722 samples += luxel[ 3 ];
2727 if ( samples == 0.0f ) {
2728 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2736 /* fix negative samples */
2737 for ( j = 0; j < 3; j++ )
2739 if ( sample[ j ] < 0.0f ) {
2746 /* scale the sample */
2747 VectorScale( sample, ( 1.0f / samples ), sample );
2749 /* store the sample in the radiosity luxels */
2751 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2752 VectorCopy( sample, radLuxel );
2754 /* if only storing bounced light, early out here */
2755 if ( bounceOnly && !bouncing ) {
2760 /* store the sample in the bsp luxels */
2761 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2762 bspDeluxel = BSP_DELUXEL( x, y );
2764 VectorAdd( bspLuxel, sample, bspLuxel );
2765 if ( deluxemap && lightmapNum == 0 ) {
2766 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2769 /* add color to bounds for solid checking */
2770 if ( samples > 0.0f ) {
2771 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2776 /* set solid color */
2777 lm->solid[ lightmapNum ] = qfalse;
2778 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2779 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2781 /* nocollapse prevents solid lightmaps */
2782 if ( noCollapse == qfalse ) {
2783 /* check solid color */
2784 VectorSubtract( colorMaxs, colorMins, sample );
2785 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2786 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2788 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2789 lm->solid[ lightmapNum ] = qtrue;
2790 numSolidLightmaps++;
2793 /* if all lightmaps aren't solid, then none of them are solid */
2794 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2795 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2797 if ( lm->solid[ y ] ) {
2798 numSolidLightmaps--;
2800 lm->solid[ y ] = qfalse;
2805 /* wrap bsp luxels if necessary */
2806 if ( lm->wrap[ 0 ] ) {
2807 for ( y = 0; y < lm->h; y++ )
2809 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2810 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2811 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2812 VectorScale( bspLuxel, 0.5f, bspLuxel );
2813 VectorCopy( bspLuxel, bspLuxel2 );
2814 if ( deluxemap && lightmapNum == 0 ) {
2815 bspDeluxel = BSP_DELUXEL( 0, y );
2816 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2817 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2818 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2819 VectorCopy( bspDeluxel, bspDeluxel2 );
2823 if ( lm->wrap[ 1 ] ) {
2824 for ( x = 0; x < lm->w; x++ )
2826 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2827 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2828 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2829 VectorScale( bspLuxel, 0.5f, bspLuxel );
2830 VectorCopy( bspLuxel, bspLuxel2 );
2831 if ( deluxemap && lightmapNum == 0 ) {
2832 bspDeluxel = BSP_DELUXEL( x, 0 );
2833 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2834 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2835 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2836 VectorCopy( bspDeluxel, bspDeluxel2 );
2843 /* -----------------------------------------------------------------
2844 convert modelspace deluxemaps to tangentspace
2845 ----------------------------------------------------------------- */
2848 if ( deluxemap && deluxemode == 1 ) {
2849 vec3_t worldUp, myNormal, myTangent, myBinormal;
2852 Sys_Printf( "converting..." );
2854 for ( i = 0; i < numRawLightmaps; i++ )
2857 lm = &rawLightmaps[ i ];
2859 /* walk lightmap samples */
2860 for ( y = 0; y < lm->sh; y++ )
2862 for ( x = 0; x < lm->sw; x++ )
2864 /* get normal and deluxel */
2865 normal = SUPER_NORMAL( x, y );
2866 cluster = SUPER_CLUSTER( x, y );
2867 bspDeluxel = BSP_DELUXEL( x, y );
2868 deluxel = SUPER_DELUXEL( x, y );
2871 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2873 /* get tangent vectors */
2874 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2875 if ( myNormal[ 2 ] == 1.0f ) {
2876 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2877 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2879 else if ( myNormal[ 2 ] == -1.0f ) {
2880 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2881 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2886 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2887 CrossProduct( myNormal, worldUp, myTangent );
2888 VectorNormalize( myTangent, myTangent );
2889 CrossProduct( myTangent, myNormal, myBinormal );
2890 VectorNormalize( myBinormal, myBinormal );
2893 /* project onto plane */
2894 dist = -DotProduct( myTangent, myNormal );
2895 VectorMA( myTangent, dist, myNormal, myTangent );
2896 dist = -DotProduct( myBinormal, myNormal );
2897 VectorMA( myBinormal, dist, myNormal, myBinormal );
2900 VectorNormalize( myTangent, myTangent );
2901 VectorNormalize( myBinormal, myBinormal );
2903 /* convert modelspace deluxel to tangentspace */
2904 dirSample[0] = bspDeluxel[0];
2905 dirSample[1] = bspDeluxel[1];
2906 dirSample[2] = bspDeluxel[2];
2907 VectorNormalize( dirSample, dirSample );
2909 /* fix tangents to world matrix */
2910 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2911 VectorNegate( myTangent, myTangent );
2914 /* build tangentspace vectors */
2915 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2916 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2917 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2924 /* -----------------------------------------------------------------
2926 ----------------------------------------------------------------- */
2928 #ifdef sdfsdfwq312323
2930 Sys_Printf( "blending..." );
2932 for ( i = 0; i < numRawLightmaps; i++ )
2938 lm = &rawLightmaps[ i ];
2940 /* walk individual lightmaps */
2941 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2944 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2948 /* walk lightmap samples */
2949 for ( y = 0; y < lm->sh; y++ )
2951 for ( x = 0; x < lm->sw; x++ )
2954 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2957 VectorNormalize( bspLuxel, myColor );
2958 myBrightness = VectorLength( bspLuxel );
2959 myBrightness *= ( 1 / 127.0f );
2960 myBrightness = myBrightness * myBrightness;
2961 myBrightness *= 127.0f;
2962 VectorScale( myColor, myBrightness, bspLuxel );
2969 /* -----------------------------------------------------------------
2970 collapse non-unique lightmaps
2971 ----------------------------------------------------------------- */
2973 if ( noCollapse == qfalse && deluxemap == qfalse ) {
2975 Sys_FPrintf( SYS_VRB, "collapsing..." );
2977 /* set all twin refs to null */
2978 for ( i = 0; i < numRawLightmaps; i++ )
2980 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2982 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2983 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2984 rawLightmaps[ i ].numStyledTwins = 0;
2988 /* walk the list of raw lightmaps */
2989 for ( i = 0; i < numRawLightmaps; i++ )
2992 lm = &rawLightmaps[ i ];
2994 /* walk lightmaps */
2995 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2998 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
2999 lm->twins[ lightmapNum ] != NULL ) {
3003 /* find all lightmaps that are virtually identical to this one */
3004 for ( j = i + 1; j < numRawLightmaps; j++ )
3007 lm2 = &rawLightmaps[ j ];
3009 /* walk lightmaps */
3010 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3013 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3014 lm2->twins[ lightmapNum2 ] != NULL ) {
3019 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3020 /* merge and set twin */
3021 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3022 lm2->twins[ lightmapNum2 ] = lm;
3023 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3025 numTwinLuxels += ( lm->w * lm->h );
3027 /* count styled twins */
3028 if ( lightmapNum > 0 ) {
3029 lm->numStyledTwins++;
3039 /* -----------------------------------------------------------------
3040 sort raw lightmaps by shader
3041 ----------------------------------------------------------------- */
3044 Sys_FPrintf( SYS_VRB, "sorting..." );
3046 /* allocate a new sorted list */
3047 if ( sortLightmaps == NULL ) {
3048 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3051 /* fill it out and sort it */
3052 for ( i = 0; i < numRawLightmaps; i++ )
3053 sortLightmaps[ i ] = i;
3054 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3056 /* -----------------------------------------------------------------
3057 allocate output lightmaps
3058 ----------------------------------------------------------------- */
3061 Sys_FPrintf( SYS_VRB, "allocating..." );
3063 /* kill all existing output lightmaps */
3064 if ( outLightmaps != NULL ) {
3065 for ( i = 0; i < numOutLightmaps; i++ )
3067 free( outLightmaps[ i ].lightBits );
3068 free( outLightmaps[ i ].bspLightBytes );
3070 free( outLightmaps );
3071 outLightmaps = NULL;
3074 numLightmapShaders = 0;
3075 numOutLightmaps = 0;
3076 numBSPLightmaps = 0;
3077 numExtLightmaps = 0;
3079 /* find output lightmap */
3080 for ( i = 0; i < numRawLightmaps; i++ )
3082 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3083 FindOutLightmaps( lm, fastAllocate );
3086 /* set output numbers in twinned lightmaps */
3087 for ( i = 0; i < numRawLightmaps; i++ )
3090 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3092 /* walk lightmaps */
3093 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3096 lm2 = lm->twins[ lightmapNum ];
3097 if ( lm2 == NULL ) {
3100 lightmapNum2 = lm->twinNums[ lightmapNum ];
3102 /* find output lightmap from twin */
3103 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3104 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3105 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3109 /* -----------------------------------------------------------------
3110 store output lightmaps
3111 ----------------------------------------------------------------- */
3114 Sys_FPrintf( SYS_VRB, "storing..." );
3116 /* count the bsp lightmaps and allocate space */
3117 if ( bspLightBytes != NULL ) {
3118 free( bspLightBytes );
3120 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3121 numBSPLightBytes = 0;
3122 bspLightBytes = NULL;
3126 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3127 bspLightBytes = safe_malloc( numBSPLightBytes );
3128 memset( bspLightBytes, 0, numBSPLightBytes );
3131 /* walk the list of output lightmaps */
3132 for ( i = 0; i < numOutLightmaps; i++ )
3134 /* get output lightmap */
3135 olm = &outLightmaps[ i ];
3137 /* fill output lightmap */
3138 if ( lightmapFill ) {
3139 FillOutLightmap( olm );
3142 /* is this a valid bsp lightmap? */
3143 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3144 /* copy lighting data */
3145 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3146 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3148 /* copy direction data */
3150 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3151 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3155 /* external lightmap? */
3156 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3157 /* make a directory for the lightmaps */
3160 /* set external lightmap number */
3161 olm->extLightmapNum = numExtLightmaps;
3163 /* write lightmap */
3164 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3165 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3166 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3169 /* write deluxemap */
3171 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3172 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3173 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3176 if ( debugDeluxemap ) {
3177 olm->extLightmapNum++;
3183 if ( numExtLightmaps > 0 ) {
3184 Sys_FPrintf( SYS_VRB, "\n" );
3187 /* delete unused external lightmaps */
3188 for ( i = numExtLightmaps; i; i++ )
3190 /* determine if file exists */
3191 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3192 if ( !FileExists( filename ) ) {
3200 /* -----------------------------------------------------------------
3201 project the lightmaps onto the bsp surfaces
3202 ----------------------------------------------------------------- */
3205 Sys_FPrintf( SYS_VRB, "projecting..." );
3207 /* walk the list of surfaces */
3208 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3210 /* get the surface and info */
3211 ds = &bspDrawSurfaces[ i ];
3212 info = &surfaceInfos[ i ];
3216 /* handle surfaces with identical parent */
3217 if ( info->parentSurfaceNum >= 0 ) {
3218 /* preserve original data and get parent */
3219 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3220 memcpy( &dsTemp, ds, sizeof( *ds ) );
3222 /* overwrite child with parent data */
3223 memcpy( ds, parent, sizeof( *ds ) );
3225 /* restore key parts */
3226 ds->fogNum = dsTemp.fogNum;
3227 ds->firstVert = dsTemp.firstVert;
3228 ds->firstIndex = dsTemp.firstIndex;
3229 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3231 /* set vertex data */
3232 dv = &bspDrawVerts[ ds->firstVert ];
3233 dvParent = &bspDrawVerts[ parent->firstVert ];
3234 for ( j = 0; j < ds->numVerts; j++ )
3236 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3237 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3244 /* handle vertex lit or approximated surfaces */
3245 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3246 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3248 ds->lightmapNum[ lightmapNum ] = -3;
3249 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3253 /* handle lightmapped surfaces */
3256 /* walk lightmaps */
3257 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3260 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3262 /* handle unused style */
3263 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3264 ds->lightmapNum[ lightmapNum ] = -3;
3268 /* get output lightmap */
3269 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3271 /* set bsp lightmap number */
3272 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3274 /* deluxemap debugging makes the deluxemap visible */
3275 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3276 ds->lightmapNum[ lightmapNum ]++;
3279 /* calc lightmap origin in texture space */
3280 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3281 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3283 /* calc lightmap st coords */
3284 dv = &bspDrawVerts[ ds->firstVert ];
3285 ydv = &yDrawVerts[ ds->firstVert ];
3286 for ( j = 0; j < ds->numVerts; j++ )
3288 if ( lm->solid[ lightmapNum ] ) {
3289 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3290 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3294 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3295 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3301 /* store vertex colors */
3302 dv = &bspDrawVerts[ ds->firstVert ];
3303 for ( j = 0; j < ds->numVerts; j++ )
3305 /* walk lightmaps */
3306 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3308 /* handle unused style */
3309 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3310 VectorClear( color );
3314 /* get vertex color */
3315 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3316 VectorCopy( luxel, color );
3318 /* set minimum light */
3319 if ( lightmapNum == 0 ) {
3320 for ( k = 0; k < 3; k++ )
3321 if ( color[ k ] < minVertexLight[ k ] ) {
3322 color[ k ] = minVertexLight[ k ];
3327 /* store to bytes */
3328 if ( !info->si->noVertexLight ) {
3329 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3334 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3335 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3337 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3341 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3342 dv = &bspDrawVerts[ ds->firstVert ];
3344 /* depthFunc equal? */
3345 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3352 /* generate stages for styled lightmaps */
3353 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3356 style = lm->styles[ lightmapNum ];
3357 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3361 /* get output lightmap */
3362 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3365 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3366 strcpy( lightmapName, "$lightmap" );
3369 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3372 /* get rgbgen string */
3373 if ( rgbGenValues[ style ] == NULL ) {
3374 sprintf( key, "_style%drgbgen", style );
3375 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3376 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3377 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3381 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3382 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3388 /* get alphagen string */
3389 if ( alphaGenValues[ style ] == NULL ) {
3390 sprintf( key, "_style%dalphagen", style );
3391 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3393 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3394 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3397 alphaGen[ 0 ] = '\0';
3400 /* calculate st offset */
3401 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3402 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3404 /* create additional stage */
3405 if ( lmx == 0.0f && lmy == 0.0f ) {
3406 sprintf( styleStage, "\t{\n"
3407 "\t\tmap %s\n" /* lightmap */
3408 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3409 "%s" /* depthFunc equal */
3412 "\t\ttcGen lightmap\n"
3415 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3421 sprintf( styleStage, "\t{\n"
3422 "\t\tmap %s\n" /* lightmap */
3423 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3424 "%s" /* depthFunc equal */
3427 "\t\ttcGen lightmap\n"
3428 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3431 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3439 strcat( styleStages, styleStage );
3442 /* create custom shader */
3443 if ( info->si->styleMarker == 2 ) {
3444 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3447 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3450 /* emit remap command */
3451 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3454 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3455 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3456 //% Sys_Printf( ")\n" );
3459 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3460 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3461 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3462 /* get output lightmap */
3463 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3465 /* do some name mangling */
3466 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3468 /* create custom shader */
3469 csi = CustomShader( info->si, "$lightmap", lightmapName );
3472 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3473 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3474 //% Sys_Printf( ")\n" );
3477 /* use the normal plain-jane shader */
3479 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3484 Sys_FPrintf( SYS_VRB, "done.\n" );
3486 /* calc num stored */
3487 numStored = numBSPLightBytes / 3;
3488 efficiency = ( numStored <= 0 )
3490 : (float) numUsed / (float) numStored;
3493 Sys_Printf( "%9d luxels used\n", numUsed );
3494 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3495 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3496 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3497 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3498 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3499 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3500 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3501 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3503 /* write map shader file */
3504 WriteMapShaderFile();