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 */
2345 void FillOutLightmap( outLightmap_t *olm ){
2348 vec3_t dir_sum, light_sum;
2350 byte *lightBitsNew = NULL;
2351 byte *lightBytesNew = NULL;
2352 byte *dirBytesNew = NULL;
2354 lightBitsNew = safe_malloc( ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2355 lightBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2357 dirBytesNew = safe_malloc( olm->customWidth * olm->customHeight * 3 );
2361 memset(olm->lightBits, 0, (olm->customWidth * olm->customHeight + 8) / 8);
2362 olm->lightBits[0] |= 1;
2363 olm->lightBits[(10 * olm->customWidth + 30) >> 3] |= 1 << ((10 * olm->customWidth + 30) & 7);
2364 memset(olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3);
2365 olm->bspLightBytes[0] = 255;
2366 olm->bspLightBytes[(10 * olm->customWidth + 30) * 3 + 2] = 255;
2369 memcpy( lightBitsNew, olm->lightBits, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2370 memcpy( lightBytesNew, olm->bspLightBytes, olm->customWidth * olm->customHeight * 3 );
2372 memcpy( dirBytesNew, olm->bspDirBytes, olm->customWidth * olm->customHeight * 3 );
2378 for ( y = 0; y < olm->customHeight; ++y )
2380 for ( x = 0; x < olm->customWidth; ++x )
2382 ofs = y * olm->customWidth + x;
2383 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2387 VectorClear( dir_sum );
2388 VectorClear( light_sum );
2390 /* try all four neighbors */
2391 ofs = ( ( y + olm->customHeight - 1 ) % olm->customHeight ) * olm->customWidth + x;
2392 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2394 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2396 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2400 ofs = ( ( y + 1 ) % olm->customHeight ) * olm->customWidth + x;
2401 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2403 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2405 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2409 ofs = y * olm->customWidth + ( x + olm->customWidth - 1 ) % olm->customWidth;
2410 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2412 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2414 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2418 ofs = y * olm->customWidth + ( x + 1 ) % olm->customWidth;
2419 if ( olm->lightBits[ofs >> 3] & ( 1 << ( ofs & 7 ) ) ) { /* already filled */
2421 VectorAdd( light_sum, olm->bspLightBytes + ofs * 3, light_sum );
2423 VectorAdd( dir_sum, olm->bspDirBytes + ofs * 3, dir_sum );
2429 ofs = y * olm->customWidth + x;
2430 lightBitsNew[ofs >> 3] |= ( 1 << ( ofs & 7 ) );
2431 VectorScale( light_sum, 1.0 / cnt, lightBytesNew + ofs * 3 );
2433 VectorScale( dir_sum, 1.0 / cnt, dirBytesNew + ofs * 3 );
2443 memcpy( olm->lightBits, lightBitsNew, ( olm->customWidth * olm->customHeight + 8 ) / 8 );
2444 memcpy( olm->bspLightBytes, lightBytesNew, olm->customWidth * olm->customHeight * 3 );
2446 memcpy( olm->bspDirBytes, dirBytesNew, olm->customWidth * olm->customHeight * 3 );
2450 free( lightBitsNew );
2451 free( lightBytesNew );
2453 free( dirBytesNew );
2458 StoreSurfaceLightmaps()
2459 stores the surface lightmaps into the bsp as byte rgb triplets
2462 void StoreSurfaceLightmaps( qboolean fastAllocate ){
2463 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2464 int style, size, lightmapNum, lightmapNum2;
2465 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2466 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2467 float *deluxel, *bspDeluxel, *bspDeluxel2;
2469 int numUsed, numTwins, numTwinLuxels, numStored;
2470 float lmx, lmy, efficiency;
2472 bspDrawSurface_t *ds, *parent, dsTemp;
2473 surfaceInfo_t *info;
2474 rawLightmap_t *lm, *lm2;
2476 bspDrawVert_t *dv, *ydv, *dvParent;
2477 char dirname[ 1024 ], filename[ 1024 ];
2479 char lightmapName[ 128 ];
2480 const char *rgbGenValues[ 256 ];
2481 const char *alphaGenValues[ 256 ];
2485 Sys_Printf( "--- StoreSurfaceLightmaps ---\n" );
2488 if ( lmCustomDir ) {
2489 strcpy( dirname, lmCustomDir );
2493 strcpy( dirname, source );
2494 StripExtension( dirname );
2496 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2497 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2499 /* -----------------------------------------------------------------
2500 average the sampled luxels into the bsp luxels
2501 ----------------------------------------------------------------- */
2504 Sys_Printf( "Subsampling..." );
2506 /* walk the list of raw lightmaps */
2510 numSolidLightmaps = 0;
2511 for ( i = 0; i < numRawLightmaps; i++ )
2514 lm = &rawLightmaps[ i ];
2516 /* walk individual lightmaps */
2517 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2520 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2524 /* allocate bsp luxel storage */
2525 if ( lm->bspLuxels[ lightmapNum ] == NULL ) {
2526 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2527 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2528 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2531 /* allocate radiosity lightmap storage */
2533 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2534 if ( lm->radLuxels[ lightmapNum ] == NULL ) {
2535 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2537 memset( lm->radLuxels[ lightmapNum ], 0, size );
2540 /* average supersampled luxels */
2541 for ( y = 0; y < lm->h; y++ )
2543 for ( x = 0; x < lm->w; x++ )
2547 occludedSamples = 0.0f;
2549 VectorClear( sample );
2550 VectorClear( occludedSample );
2551 VectorClear( dirSample );
2552 for ( ly = 0; ly < superSample; ly++ )
2554 for ( lx = 0; lx < superSample; lx++ )
2557 sx = x * superSample + lx;
2558 sy = y * superSample + ly;
2559 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2560 deluxel = SUPER_DELUXEL( sx, sy );
2561 normal = SUPER_NORMAL( sx, sy );
2562 cluster = SUPER_CLUSTER( sx, sy );
2564 /* sample deluxemap */
2565 if ( deluxemap && lightmapNum == 0 ) {
2566 VectorAdd( dirSample, deluxel, dirSample );
2569 /* keep track of used/occluded samples */
2570 if ( *cluster != CLUSTER_UNMAPPED ) {
2574 /* handle lightmap border? */
2575 if ( lightmapBorder && ( sx == 0 || sx == ( lm->sw - 1 ) || sy == 0 || sy == ( lm->sh - 1 ) ) && luxel[ 3 ] > 0.0f ) {
2576 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2581 else if ( debug && *cluster < 0 ) {
2582 if ( *cluster == CLUSTER_UNMAPPED ) {
2583 VectorSet( luxel, 255, 204, 0 );
2585 else if ( *cluster == CLUSTER_OCCLUDED ) {
2586 VectorSet( luxel, 255, 0, 255 );
2588 else if ( *cluster == CLUSTER_FLOODED ) {
2589 VectorSet( luxel, 0, 32, 255 );
2591 VectorAdd( occludedSample, luxel, occludedSample );
2592 occludedSamples += 1.0f;
2595 /* normal luxel handling */
2596 else if ( luxel[ 3 ] > 0.0f ) {
2597 /* handle lit or flooded luxels */
2598 if ( *cluster > 0 || *cluster == CLUSTER_FLOODED ) {
2599 VectorAdd( sample, luxel, sample );
2600 samples += luxel[ 3 ];
2603 /* handle occluded or unmapped luxels */
2606 VectorAdd( occludedSample, luxel, occludedSample );
2607 occludedSamples += luxel[ 3 ];
2610 /* handle style debugging */
2611 if ( debug && lightmapNum > 0 && x < 2 && y < 2 ) {
2612 VectorCopy( debugColors[ 0 ], sample );
2619 /* only use occluded samples if necessary */
2620 if ( samples <= 0.0f ) {
2621 VectorCopy( occludedSample, sample );
2622 samples = occludedSamples;
2626 luxel = SUPER_LUXEL( lightmapNum, x, y );
2627 deluxel = SUPER_DELUXEL( x, y );
2629 /* store light direction */
2630 if ( deluxemap && lightmapNum == 0 ) {
2631 VectorCopy( dirSample, deluxel );
2634 /* store the sample back in super luxels */
2635 if ( samples > 0.01f ) {
2636 VectorScale( sample, ( 1.0f / samples ), luxel );
2640 /* if any samples were mapped in any way, store ambient color */
2641 else if ( mappedSamples > 0 ) {
2642 if ( lightmapNum == 0 ) {
2643 VectorCopy( ambientColor, luxel );
2646 VectorClear( luxel );
2651 /* store a bogus value to be fixed later */
2654 VectorClear( luxel );
2662 ClearBounds( colorMins, colorMaxs );
2664 /* clean up and store into bsp luxels */
2665 for ( y = 0; y < lm->h; y++ )
2667 for ( x = 0; x < lm->w; x++ )
2670 luxel = SUPER_LUXEL( lightmapNum, x, y );
2671 deluxel = SUPER_DELUXEL( x, y );
2673 /* copy light direction */
2674 if ( deluxemap && lightmapNum == 0 ) {
2675 VectorCopy( deluxel, dirSample );
2678 /* is this a valid sample? */
2679 if ( luxel[ 3 ] > 0.0f ) {
2680 VectorCopy( luxel, sample );
2681 samples = luxel[ 3 ];
2685 /* fix negative samples */
2686 for ( j = 0; j < 3; j++ )
2688 if ( sample[ j ] < 0.0f ) {
2695 /* nick an average value from the neighbors */
2696 VectorClear( sample );
2697 VectorClear( dirSample );
2700 /* fixme: why is this disabled?? */
2701 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2703 if ( sy < 0 || sy >= lm->h ) {
2707 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2709 if ( sx < 0 || sx >= lm->w || ( sx == x && sy == y ) ) {
2713 /* get neighbor's particulars */
2714 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2715 if ( luxel[ 3 ] < 0.0f ) {
2718 VectorAdd( sample, luxel, sample );
2719 samples += luxel[ 3 ];
2724 if ( samples == 0.0f ) {
2725 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2733 /* fix negative samples */
2734 for ( j = 0; j < 3; j++ )
2736 if ( sample[ j ] < 0.0f ) {
2743 /* scale the sample */
2744 VectorScale( sample, ( 1.0f / samples ), sample );
2746 /* store the sample in the radiosity luxels */
2748 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2749 VectorCopy( sample, radLuxel );
2751 /* if only storing bounced light, early out here */
2752 if ( bounceOnly && !bouncing ) {
2757 /* store the sample in the bsp luxels */
2758 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2759 bspDeluxel = BSP_DELUXEL( x, y );
2761 VectorAdd( bspLuxel, sample, bspLuxel );
2762 if ( deluxemap && lightmapNum == 0 ) {
2763 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2766 /* add color to bounds for solid checking */
2767 if ( samples > 0.0f ) {
2768 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2773 /* set solid color */
2774 lm->solid[ lightmapNum ] = qfalse;
2775 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2776 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2778 /* nocollapse prevents solid lightmaps */
2779 if ( noCollapse == qfalse ) {
2780 /* check solid color */
2781 VectorSubtract( colorMaxs, colorMins, sample );
2782 if ( ( sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON ) ||
2783 ( lm->w <= 2 && lm->h <= 2 ) ) { /* small lightmaps get forced to solid color */
2785 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2786 lm->solid[ lightmapNum ] = qtrue;
2787 numSolidLightmaps++;
2790 /* if all lightmaps aren't solid, then none of them are solid */
2791 if ( lm->solid[ lightmapNum ] != lm->solid[ 0 ] ) {
2792 for ( y = 0; y < MAX_LIGHTMAPS; y++ )
2794 if ( lm->solid[ y ] ) {
2795 numSolidLightmaps--;
2797 lm->solid[ y ] = qfalse;
2802 /* wrap bsp luxels if necessary */
2803 if ( lm->wrap[ 0 ] ) {
2804 for ( y = 0; y < lm->h; y++ )
2806 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2807 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2808 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2809 VectorScale( bspLuxel, 0.5f, bspLuxel );
2810 VectorCopy( bspLuxel, bspLuxel2 );
2811 if ( deluxemap && lightmapNum == 0 ) {
2812 bspDeluxel = BSP_DELUXEL( 0, y );
2813 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2814 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2815 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2816 VectorCopy( bspDeluxel, bspDeluxel2 );
2820 if ( lm->wrap[ 1 ] ) {
2821 for ( x = 0; x < lm->w; x++ )
2823 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2824 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2825 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2826 VectorScale( bspLuxel, 0.5f, bspLuxel );
2827 VectorCopy( bspLuxel, bspLuxel2 );
2828 if ( deluxemap && lightmapNum == 0 ) {
2829 bspDeluxel = BSP_DELUXEL( x, 0 );
2830 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2831 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2832 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2833 VectorCopy( bspDeluxel, bspDeluxel2 );
2840 /* -----------------------------------------------------------------
2841 convert modelspace deluxemaps to tangentspace
2842 ----------------------------------------------------------------- */
2845 if ( deluxemap && deluxemode == 1 ) {
2846 vec3_t worldUp, myNormal, myTangent, myBinormal;
2849 Sys_Printf( "converting..." );
2851 for ( i = 0; i < numRawLightmaps; i++ )
2854 lm = &rawLightmaps[ i ];
2856 /* walk lightmap samples */
2857 for ( y = 0; y < lm->sh; y++ )
2859 for ( x = 0; x < lm->sw; x++ )
2861 /* get normal and deluxel */
2862 normal = SUPER_NORMAL( x, y );
2863 cluster = SUPER_CLUSTER( x, y );
2864 bspDeluxel = BSP_DELUXEL( x, y );
2865 deluxel = SUPER_DELUXEL( x, y );
2868 VectorSet( myNormal, normal[0], normal[1], normal[2] );
2870 /* get tangent vectors */
2871 if ( myNormal[ 0 ] == 0.0f && myNormal[ 1 ] == 0.0f ) {
2872 if ( myNormal[ 2 ] == 1.0f ) {
2873 VectorSet( myTangent, 1.0f, 0.0f, 0.0f );
2874 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2876 else if ( myNormal[ 2 ] == -1.0f ) {
2877 VectorSet( myTangent, -1.0f, 0.0f, 0.0f );
2878 VectorSet( myBinormal, 0.0f, 1.0f, 0.0f );
2883 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
2884 CrossProduct( myNormal, worldUp, myTangent );
2885 VectorNormalize( myTangent, myTangent );
2886 CrossProduct( myTangent, myNormal, myBinormal );
2887 VectorNormalize( myBinormal, myBinormal );
2890 /* project onto plane */
2891 dist = -DotProduct( myTangent, myNormal );
2892 VectorMA( myTangent, dist, myNormal, myTangent );
2893 dist = -DotProduct( myBinormal, myNormal );
2894 VectorMA( myBinormal, dist, myNormal, myBinormal );
2897 VectorNormalize( myTangent, myTangent );
2898 VectorNormalize( myBinormal, myBinormal );
2900 /* convert modelspace deluxel to tangentspace */
2901 dirSample[0] = bspDeluxel[0];
2902 dirSample[1] = bspDeluxel[1];
2903 dirSample[2] = bspDeluxel[2];
2904 VectorNormalize( dirSample, dirSample );
2906 /* fix tangents to world matrix */
2907 if ( myNormal[0] > 0 || myNormal[1] < 0 || myNormal[2] < 0 ) {
2908 VectorNegate( myTangent, myTangent );
2911 /* build tangentspace vectors */
2912 bspDeluxel[0] = DotProduct( dirSample, myTangent );
2913 bspDeluxel[1] = DotProduct( dirSample, myBinormal );
2914 bspDeluxel[2] = DotProduct( dirSample, myNormal );
2921 /* -----------------------------------------------------------------
2923 ----------------------------------------------------------------- */
2925 #ifdef sdfsdfwq312323
2927 Sys_Printf( "blending..." );
2929 for ( i = 0; i < numRawLightmaps; i++ )
2935 lm = &rawLightmaps[ i ];
2937 /* walk individual lightmaps */
2938 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2941 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2945 /* walk lightmap samples */
2946 for ( y = 0; y < lm->sh; y++ )
2948 for ( x = 0; x < lm->sw; x++ )
2951 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2954 VectorNormalize( bspLuxel, myColor );
2955 myBrightness = VectorLength( bspLuxel );
2956 myBrightness *= ( 1 / 127.0f );
2957 myBrightness = myBrightness * myBrightness;
2958 myBrightness *= 127.0f;
2959 VectorScale( myColor, myBrightness, bspLuxel );
2966 /* -----------------------------------------------------------------
2967 collapse non-unique lightmaps
2968 ----------------------------------------------------------------- */
2970 if ( noCollapse == qfalse && deluxemap == qfalse ) {
2972 Sys_Printf( "collapsing..." );
2974 /* set all twin refs to null */
2975 for ( i = 0; i < numRawLightmaps; i++ )
2977 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2979 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2980 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2981 rawLightmaps[ i ].numStyledTwins = 0;
2985 /* walk the list of raw lightmaps */
2986 for ( i = 0; i < numRawLightmaps; i++ )
2989 lm = &rawLightmaps[ i ];
2991 /* walk lightmaps */
2992 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2995 if ( lm->bspLuxels[ lightmapNum ] == NULL ||
2996 lm->twins[ lightmapNum ] != NULL ) {
3000 /* find all lightmaps that are virtually identical to this one */
3001 for ( j = i + 1; j < numRawLightmaps; j++ )
3004 lm2 = &rawLightmaps[ j ];
3006 /* walk lightmaps */
3007 for ( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
3010 if ( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
3011 lm2->twins[ lightmapNum2 ] != NULL ) {
3016 if ( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3017 /* merge and set twin */
3018 if ( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) ) {
3019 lm2->twins[ lightmapNum2 ] = lm;
3020 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
3022 numTwinLuxels += ( lm->w * lm->h );
3024 /* count styled twins */
3025 if ( lightmapNum > 0 ) {
3026 lm->numStyledTwins++;
3036 /* -----------------------------------------------------------------
3037 sort raw lightmaps by shader
3038 ----------------------------------------------------------------- */
3041 Sys_Printf( "sorting..." );
3043 /* allocate a new sorted list */
3044 if ( sortLightmaps == NULL ) {
3045 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
3048 /* fill it out and sort it */
3049 for ( i = 0; i < numRawLightmaps; i++ )
3050 sortLightmaps[ i ] = i;
3051 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
3053 /* -----------------------------------------------------------------
3054 allocate output lightmaps
3055 ----------------------------------------------------------------- */
3058 Sys_Printf( "allocating..." );
3060 /* kill all existing output lightmaps */
3061 if ( outLightmaps != NULL ) {
3062 for ( i = 0; i < numOutLightmaps; i++ )
3064 free( outLightmaps[ i ].lightBits );
3065 free( outLightmaps[ i ].bspLightBytes );
3067 free( outLightmaps );
3068 outLightmaps = NULL;
3071 numLightmapShaders = 0;
3072 numOutLightmaps = 0;
3073 numBSPLightmaps = 0;
3074 numExtLightmaps = 0;
3076 /* find output lightmap */
3077 for ( i = 0; i < numRawLightmaps; i++ )
3079 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3080 FindOutLightmaps( lm, fastAllocate );
3083 /* set output numbers in twinned lightmaps */
3084 for ( i = 0; i < numRawLightmaps; i++ )
3087 lm = &rawLightmaps[ sortLightmaps[ i ] ];
3089 /* walk lightmaps */
3090 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3093 lm2 = lm->twins[ lightmapNum ];
3094 if ( lm2 == NULL ) {
3097 lightmapNum2 = lm->twinNums[ lightmapNum ];
3099 /* find output lightmap from twin */
3100 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
3101 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
3102 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
3106 /* -----------------------------------------------------------------
3107 store output lightmaps
3108 ----------------------------------------------------------------- */
3111 Sys_Printf( "storing..." );
3113 /* count the bsp lightmaps and allocate space */
3114 if ( bspLightBytes != NULL ) {
3115 free( bspLightBytes );
3117 if ( numBSPLightmaps == 0 || externalLightmaps ) {
3118 numBSPLightBytes = 0;
3119 bspLightBytes = NULL;
3123 numBSPLightBytes = ( numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3 );
3124 bspLightBytes = safe_malloc( numBSPLightBytes );
3125 memset( bspLightBytes, 0, numBSPLightBytes );
3128 /* walk the list of output lightmaps */
3129 for ( i = 0; i < numOutLightmaps; i++ )
3131 /* get output lightmap */
3132 olm = &outLightmaps[ i ];
3134 /* fill output lightmap */
3135 if ( lightmapFill ) {
3136 FillOutLightmap( olm );
3139 /* is this a valid bsp lightmap? */
3140 if ( olm->lightmapNum >= 0 && !externalLightmaps ) {
3141 /* copy lighting data */
3142 lb = bspLightBytes + ( olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3 );
3143 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
3145 /* copy direction data */
3147 lb = bspLightBytes + ( ( olm->lightmapNum + 1 ) * game->lightmapSize * game->lightmapSize * 3 );
3148 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
3152 /* external lightmap? */
3153 if ( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps ) {
3154 /* make a directory for the lightmaps */
3157 /* set external lightmap number */
3158 olm->extLightmapNum = numExtLightmaps;
3160 /* write lightmap */
3161 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3162 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3163 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
3166 /* write deluxemap */
3168 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
3169 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
3170 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
3173 if ( debugDeluxemap ) {
3174 olm->extLightmapNum++;
3180 if ( numExtLightmaps > 0 ) {
3184 /* delete unused external lightmaps */
3185 for ( i = numExtLightmaps; i; i++ )
3187 /* determine if file exists */
3188 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
3189 if ( !FileExists( filename ) ) {
3197 /* -----------------------------------------------------------------
3198 project the lightmaps onto the bsp surfaces
3199 ----------------------------------------------------------------- */
3202 Sys_Printf( "projecting..." );
3204 /* walk the list of surfaces */
3205 for ( i = 0; i < numBSPDrawSurfaces; i++ )
3207 /* get the surface and info */
3208 ds = &bspDrawSurfaces[ i ];
3209 info = &surfaceInfos[ i ];
3213 /* handle surfaces with identical parent */
3214 if ( info->parentSurfaceNum >= 0 ) {
3215 /* preserve original data and get parent */
3216 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
3217 memcpy( &dsTemp, ds, sizeof( *ds ) );
3219 /* overwrite child with parent data */
3220 memcpy( ds, parent, sizeof( *ds ) );
3222 /* restore key parts */
3223 ds->fogNum = dsTemp.fogNum;
3224 ds->firstVert = dsTemp.firstVert;
3225 ds->firstIndex = dsTemp.firstIndex;
3226 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
3228 /* set vertex data */
3229 dv = &bspDrawVerts[ ds->firstVert ];
3230 dvParent = &bspDrawVerts[ parent->firstVert ];
3231 for ( j = 0; j < ds->numVerts; j++ )
3233 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
3234 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
3241 /* handle vertex lit or approximated surfaces */
3242 else if ( lm == NULL || lm->outLightmapNums[ 0 ] < 0 ) {
3243 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3245 ds->lightmapNum[ lightmapNum ] = -3;
3246 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
3250 /* handle lightmapped surfaces */
3253 /* walk lightmaps */
3254 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3257 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3259 /* handle unused style */
3260 if ( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3261 ds->lightmapNum[ lightmapNum ] = -3;
3265 /* get output lightmap */
3266 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3268 /* set bsp lightmap number */
3269 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
3271 /* deluxemap debugging makes the deluxemap visible */
3272 if ( deluxemap && debugDeluxemap && lightmapNum == 0 ) {
3273 ds->lightmapNum[ lightmapNum ]++;
3276 /* calc lightmap origin in texture space */
3277 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
3278 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
3280 /* calc lightmap st coords */
3281 dv = &bspDrawVerts[ ds->firstVert ];
3282 ydv = &yDrawVerts[ ds->firstVert ];
3283 for ( j = 0; j < ds->numVerts; j++ )
3285 if ( lm->solid[ lightmapNum ] ) {
3286 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( 0.5f / (float) olm->customWidth );
3287 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( 0.5f / (float) olm->customWidth );
3291 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + ( ydv[ j ].lightmap[ 0 ][ 0 ] / ( superSample * olm->customWidth ) );
3292 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + ( ydv[ j ].lightmap[ 0 ][ 1 ] / ( superSample * olm->customHeight ) );
3298 /* store vertex colors */
3299 dv = &bspDrawVerts[ ds->firstVert ];
3300 for ( j = 0; j < ds->numVerts; j++ )
3302 /* walk lightmaps */
3303 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3305 /* handle unused style */
3306 if ( ds->vertexStyles[ lightmapNum ] == LS_NONE ) {
3307 VectorClear( color );
3311 /* get vertex color */
3312 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
3313 VectorCopy( luxel, color );
3315 /* set minimum light */
3316 if ( lightmapNum == 0 ) {
3317 for ( k = 0; k < 3; k++ )
3318 if ( color[ k ] < minVertexLight[ k ] ) {
3319 color[ k ] = minVertexLight[ k ];
3324 /* store to bytes */
3325 if ( !info->si->noVertexLight ) {
3326 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
3331 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
3332 if ( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) { //% info->si->styleMarker > 0 )
3334 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
3338 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
3339 dv = &bspDrawVerts[ ds->firstVert ];
3341 /* depthFunc equal? */
3342 if ( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED ) {
3349 /* generate stages for styled lightmaps */
3350 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3353 style = lm->styles[ lightmapNum ];
3354 if ( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 ) {
3358 /* get output lightmap */
3359 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
3362 if ( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] ) {
3363 strcpy( lightmapName, "$lightmap" );
3366 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3369 /* get rgbgen string */
3370 if ( rgbGenValues[ style ] == NULL ) {
3371 sprintf( key, "_style%drgbgen", style );
3372 rgbGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3373 if ( rgbGenValues[ style ][ 0 ] == '\0' ) {
3374 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3378 if ( rgbGenValues[ style ][ 0 ] != '\0' ) {
3379 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3385 /* get alphagen string */
3386 if ( alphaGenValues[ style ] == NULL ) {
3387 sprintf( key, "_style%dalphagen", style );
3388 alphaGenValues[ style ] = ValueForKey( &entities[ 0 ], key );
3390 if ( alphaGenValues[ style ][ 0 ] != '\0' ) {
3391 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3394 alphaGen[ 0 ] = '\0';
3397 /* calculate st offset */
3398 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3399 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3401 /* create additional stage */
3402 if ( lmx == 0.0f && lmy == 0.0f ) {
3403 sprintf( styleStage, "\t{\n"
3404 "\t\tmap %s\n" /* lightmap */
3405 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3406 "%s" /* depthFunc equal */
3409 "\t\ttcGen lightmap\n"
3412 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3418 sprintf( styleStage, "\t{\n"
3419 "\t\tmap %s\n" /* lightmap */
3420 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3421 "%s" /* depthFunc equal */
3424 "\t\ttcGen lightmap\n"
3425 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3428 ( dfEqual ? "\t\tdepthFunc equal\n" : "" ),
3436 strcat( styleStages, styleStage );
3439 /* create custom shader */
3440 if ( info->si->styleMarker == 2 ) {
3441 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3444 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3447 /* emit remap command */
3448 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3451 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3452 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3453 //% Sys_Printf( ")\n" );
3456 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3457 else if ( olm != NULL && lm != NULL && !externalLightmaps &&
3458 ( olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize ) ) {
3459 /* get output lightmap */
3460 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3462 /* do some name mangling */
3463 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3465 /* create custom shader */
3466 csi = CustomShader( info->si, "$lightmap", lightmapName );
3469 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3470 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3471 //% Sys_Printf( ")\n" );
3474 /* use the normal plain-jane shader */
3476 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3481 Sys_Printf( "done.\n" );
3483 /* calc num stored */
3484 numStored = numBSPLightBytes / 3;
3485 efficiency = ( numStored <= 0 )
3487 : (float) numUsed / (float) numStored;
3490 Sys_Printf( "%9d luxels used\n", numUsed );
3491 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3492 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3493 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3494 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3495 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3496 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3497 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3498 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3500 /* write map shader file */
3501 WriteMapShaderFile();