2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
21 ----------------------------------------------------------------------------------
\r
23 This code has been altered significantly from its original form, to support
\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."
\r
26 ------------------------------------------------------------------------------- */
\r
31 #define LIGHTMAPS_YDNAR_C
\r
41 /* -------------------------------------------------------------------------------
\r
43 this file contains code that doe lightmap allocation and projection that
\r
44 runs in the -light phase.
\r
46 this is handled here rather than in the bsp phase for a few reasons--
\r
47 surfaces are no longer necessarily convex polygons, patches may or may not be
\r
48 planar or have lightmaps projected directly onto control points.
\r
50 also, this allows lightmaps to be calculated before being allocated and stored
\r
51 in the bsp. lightmaps that have little high-frequency information are candidates
\r
52 for having their resolutions scaled down.
\r
54 ------------------------------------------------------------------------------- */
\r
58 based on WriteTGA() from imagelib.c
\r
61 void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip )
\r
68 /* allocate a buffer and set it up */
\r
69 buffer = safe_malloc( width * height * 3 + 18 );
\r
70 memset( buffer, 0, 18 );
\r
72 buffer[ 12 ] = width & 255;
\r
73 buffer[ 13 ] = width >> 8;
\r
74 buffer[ 14 ] = height & 255;
\r
75 buffer[ 15 ] = height >> 8;
\r
78 /* swap rgb to bgr */
\r
79 c = (width * height * 3) + 18;
\r
80 for( i = 18; i < c; i += 3 )
\r
82 buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
\r
83 buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
\r
84 buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
\r
87 /* write it and free the buffer */
\r
88 file = fopen( filename, "wb" );
\r
90 Error( "Unable to open %s for writing", filename );
\r
92 /* flip vertically? */
\r
95 fwrite( buffer, 1, 18, file );
\r
96 for( in = buffer + ((height - 1) * width * 3) + 18; in >= buffer; in -= (width * 3) )
\r
97 fwrite( in, 1, (width * 3), file );
\r
100 fwrite( buffer, 1, c, file );
\r
102 /* close the file */
\r
111 exports the lightmaps as a list of numbered tga images
\r
114 void ExportLightmaps( void )
\r
117 char dirname[ 1024 ], filename[ 1024 ];
\r
122 Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n");
\r
124 /* do some path mangling */
\r
125 strcpy( dirname, source );
\r
126 StripExtension( dirname );
\r
129 if( bspLightBytes == NULL )
\r
131 Sys_Printf( "WARNING: No BSP lightmap data\n" );
\r
135 /* make a directory for the lightmaps */
\r
136 Q_mkdir( dirname );
\r
138 /* iterate through the lightmaps */
\r
139 for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) )
\r
141 /* write a tga image out */
\r
142 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
\r
143 Sys_Printf( "Writing %s\n", filename );
\r
144 WriteTGA24( filename, lightmap, LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT, qfalse );
\r
151 ExportLightmapsMain()
\r
152 exports the lightmaps as a list of numbered tga images
\r
155 int ExportLightmapsMain( int argc, char **argv )
\r
160 Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
\r
164 /* do some path mangling */
\r
165 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
\r
166 StripExtension( source );
\r
167 DefaultExtension( source, ".bsp" );
\r
170 Sys_Printf( "Loading %s\n", source );
\r
171 LoadBSPFile( source );
\r
173 /* export the lightmaps */
\r
176 /* return to sender */
\r
183 ImportLightmapsMain()
\r
184 imports the lightmaps from a list of numbered tga images
\r
187 int ImportLightmapsMain( int argc, char **argv )
\r
189 int i, x, y, len, width, height;
\r
190 char dirname[ 1024 ], filename[ 1024 ];
\r
191 byte *lightmap, *buffer, *pixels, *in, *out;
\r
197 Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
\r
201 /* do some path mangling */
\r
202 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
\r
203 StripExtension( source );
\r
204 DefaultExtension( source, ".bsp" );
\r
207 Sys_Printf( "Loading %s\n", source );
\r
208 LoadBSPFile( source );
\r
211 Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n");
\r
213 /* do some path mangling */
\r
214 strcpy( dirname, source );
\r
215 StripExtension( dirname );
\r
218 if( bspLightBytes == NULL )
\r
219 Error( "No lightmap data" );
\r
221 /* make a directory for the lightmaps */
\r
222 Q_mkdir( dirname );
\r
224 /* iterate through the lightmaps */
\r
225 for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3) )
\r
227 /* read a tga image */
\r
228 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
\r
229 Sys_Printf( "Loading %s\n", filename );
\r
231 len = vfsLoadFile( filename, (void*) &buffer, -1 );
\r
234 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
\r
238 /* parse file into an image */
\r
240 LoadTGABuffer( buffer, &pixels, &width, &height );
\r
243 /* sanity check it */
\r
244 if( pixels == NULL )
\r
246 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
\r
249 if( width != LIGHTMAP_WIDTH || height != LIGHTMAP_HEIGHT )
\r
250 Sys_Printf( "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
\r
251 filename, width, height, LIGHTMAP_WIDTH, LIGHTMAP_HEIGHT );
\r
253 /* copy the pixels */
\r
255 for( y = 1; y <= LIGHTMAP_HEIGHT; y++ )
\r
257 out = lightmap + ((LIGHTMAP_HEIGHT - y) * LIGHTMAP_WIDTH * 3);
\r
258 for( x = 0; x < LIGHTMAP_WIDTH; x++, in += 4, out += 3 )
\r
259 VectorCopy( in, out );
\r
262 /* free the image */
\r
266 /* write the bsp */
\r
267 Sys_Printf( "writing %s\n", source );
\r
268 WriteBSPFile( source );
\r
270 /* return to sender */
\r
276 /* -------------------------------------------------------------------------------
\r
278 this section deals with projecting a lightmap onto a raw drawsurface
\r
280 ------------------------------------------------------------------------------- */
\r
283 CompareLightSurface()
\r
284 compare function for qsort()
\r
287 static int CompareLightSurface( const void *a, const void *b )
\r
289 shaderInfo_t *asi, *bsi;
\r
293 asi = surfaceInfos[ *((int*) a) ].si;
\r
294 bsi = surfaceInfos[ *((int*) b) ].si;
\r
302 /* compare shader names */
\r
303 return strcmp( asi->shader, bsi->shader );
\r
309 FinishRawLightmap()
\r
310 allocates a raw lightmap's necessary buffers
\r
313 void FinishRawLightmap( rawLightmap_t *lm )
\r
315 int i, j, c, size, *sc;
\r
317 surfaceInfo_t *info;
\r
320 /* sort light surfaces by shader name */
\r
321 qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
\r
323 /* count clusters */
\r
324 lm->numLightClusters = 0;
\r
325 for( i = 0; i < lm->numLightSurfaces; i++ )
\r
327 /* get surface info */
\r
328 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
\r
330 /* add surface clusters */
\r
331 lm->numLightClusters += info->numSurfaceClusters;
\r
334 /* allocate buffer for clusters and copy */
\r
335 lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
\r
337 for( i = 0; i < lm->numLightSurfaces; i++ )
\r
339 /* get surface info */
\r
340 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
\r
342 /* add surface clusters */
\r
343 for( j = 0; j < info->numSurfaceClusters; j++ )
\r
344 lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
\r
348 lm->styles[ 0 ] = LS_NORMAL;
\r
349 for( i = 1; i < MAX_LIGHTMAPS; i++ )
\r
350 lm->styles[ i ] = LS_NONE;
\r
352 /* set supersampling size */
\r
353 lm->sw = lm->w * superSample;
\r
354 lm->sh = lm->h * superSample;
\r
356 /* add to super luxel count */
\r
357 numRawSuperLuxels += (lm->sw * lm->sh);
\r
359 /* manipulate origin/vecs for supersampling */
\r
360 if( superSample > 1 && lm->vecs != NULL )
\r
362 /* calc inverse supersample */
\r
363 is = 1.0f / superSample;
\r
365 /* scale the vectors and shift the origin */
\r
367 /* new code that works for arbitrary supersampling values */
\r
368 VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
\r
369 VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
\r
370 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
\r
371 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
\r
372 VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
\r
373 VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
\r
375 /* old code that only worked with a value of 2 */
\r
376 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
\r
377 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
\r
378 VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
\r
379 VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
\r
383 /* allocate bsp lightmap storage */
\r
384 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
\r
385 if( lm->bspLuxels[ 0 ] == NULL )
\r
386 lm->bspLuxels[ 0 ] = safe_malloc( size );
\r
387 memset( lm->bspLuxels[ 0 ], 0, size );
\r
389 /* allocate radiosity lightmap storage */
\r
392 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
\r
393 if( lm->radLuxels[ 0 ] == NULL )
\r
394 lm->radLuxels[ 0 ] = safe_malloc( size );
\r
395 memset( lm->radLuxels[ 0 ], 0, size );
\r
398 /* allocate sampling lightmap storage */
\r
399 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
\r
400 if( lm->superLuxels[ 0 ] == NULL )
\r
401 lm->superLuxels[ 0 ] = safe_malloc( size );
\r
402 memset( lm->superLuxels[ 0 ], 0, size );
\r
404 /* allocate origin map storage */
\r
405 size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
\r
406 if( lm->superOrigins == NULL )
\r
407 lm->superOrigins = safe_malloc( size );
\r
408 memset( lm->superOrigins, 0, size );
\r
410 /* allocate normal map storage */
\r
411 size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
\r
412 if( lm->superNormals == NULL )
\r
413 lm->superNormals = safe_malloc( size );
\r
414 memset( lm->superNormals, 0, size );
\r
416 /* allocate cluster map storage */
\r
417 size = lm->sw * lm->sh * sizeof( int );
\r
418 if( lm->superClusters == NULL )
\r
419 lm->superClusters = safe_malloc( size );
\r
420 size = lm->sw * lm->sh;
\r
421 sc = lm->superClusters;
\r
422 for( i = 0; i < size; i++ )
\r
423 (*sc++) = CLUSTER_UNMAPPED;
\r
425 /* deluxemap allocation */
\r
428 /* allocate sampling deluxel storage */
\r
429 size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
\r
430 if( lm->superDeluxels == NULL )
\r
431 lm->superDeluxels = safe_malloc( size );
\r
432 memset( lm->superDeluxels, 0, size );
\r
434 /* allocate bsp deluxel storage */
\r
435 size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
\r
436 if( lm->bspDeluxels == NULL )
\r
437 lm->bspDeluxels = safe_malloc( size );
\r
438 memset( lm->bspDeluxels, 0, size );
\r
442 numLuxels += (lm->sw * lm->sh);
\r
448 AddPatchToRawLightmap()
\r
449 projects a lightmap for a patch surface
\r
450 since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
\r
451 it is no longer necessary for patch verts to fall exactly on a lightmap sample
\r
452 based on AllocateLightmapForPatch()
\r
455 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm )
\r
457 bspDrawSurface_t *ds;
\r
458 surfaceInfo_t *info;
\r
460 bspDrawVert_t *verts, *a, *b;
\r
462 mesh_t src, *subdivided, *mesh;
\r
463 float sBasis, tBasis, s, t;
\r
464 float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
\r
467 /* patches finish a raw lightmap */
\r
468 lm->finished = qtrue;
\r
470 /* get surface and info */
\r
471 ds = &bspDrawSurfaces[ num ];
\r
472 info = &surfaceInfos[ num ];
\r
474 /* make a temporary mesh from the drawsurf */
\r
475 src.width = ds->patchWidth;
\r
476 src.height = ds->patchHeight;
\r
477 src.verts = &yDrawVerts[ ds->firstVert ];
\r
478 //% subdivided = SubdivideMesh( src, 8, 512 );
\r
479 subdivided = SubdivideMesh2( src, info->patchIterations );
\r
481 /* fit it to the curve and remove colinear verts on rows/columns */
\r
482 PutMeshOnCurve( *subdivided );
\r
483 mesh = RemoveLinearMeshColumnsRows( subdivided );
\r
484 FreeMesh( subdivided );
\r
486 /* find the longest distance on each row/column */
\r
487 verts = mesh->verts;
\r
488 memset( widthTable, 0, sizeof( widthTable ) );
\r
489 memset( heightTable, 0, sizeof( heightTable ) );
\r
490 for( y = 0; y < mesh->height; y++ )
\r
492 for( x = 0; x < mesh->width; x++ )
\r
495 if( x + 1 < mesh->width )
\r
497 a = &verts[ (y * mesh->width) + x ];
\r
498 b = &verts[ (y * mesh->width) + x + 1 ];
\r
499 VectorSubtract( a->xyz, b->xyz, delta );
\r
500 length = VectorLength( delta );
\r
501 if( length > widthTable[ x ] )
\r
502 widthTable[ x ] = length;
\r
506 if( y + 1 < mesh->height )
\r
508 a = &verts[ (y * mesh->width) + x ];
\r
509 b = &verts[ ((y + 1) * mesh->width) + x ];
\r
510 VectorSubtract( a->xyz, b->xyz, delta );
\r
511 length = VectorLength( delta );
\r
512 if( length > heightTable[ y ] )
\r
513 heightTable[ y ] = length;
\r
518 /* determine lightmap width */
\r
520 for( x = 0; x < (mesh->width - 1); x++ )
\r
521 length += widthTable[ x ];
\r
522 lm->w = ceil( length / lm->sampleSize ) + 1;
\r
523 if( lm->w < ds->patchWidth )
\r
524 lm->w = ds->patchWidth;
\r
525 if( lm->w > lm->customWidth )
\r
526 lm->w = lm->customWidth;
\r
527 sBasis = (float) (lm->w - 1) / (float) (ds->patchWidth - 1);
\r
529 /* determine lightmap height */
\r
531 for( y = 0; y < (mesh->height - 1); y++ )
\r
532 length += heightTable[ y ];
\r
533 lm->h = ceil( length / lm->sampleSize ) + 1;
\r
534 if( lm->h < ds->patchHeight )
\r
535 lm->h = ds->patchHeight;
\r
536 if( lm->h > lm->customHeight )
\r
537 lm->h = lm->customHeight;
\r
538 tBasis = (float) (lm->h - 1) / (float) (ds->patchHeight - 1);
\r
540 /* free the temporary mesh */
\r
543 /* set the lightmap texture coordinates in yDrawVerts */
\r
544 lm->wrap[ 0 ] = qtrue;
\r
545 lm->wrap[ 1 ] = qtrue;
\r
546 verts = &yDrawVerts[ ds->firstVert ];
\r
547 for( y = 0; y < ds->patchHeight; y++ )
\r
549 t = (tBasis * y) + 0.5f;
\r
550 for( x = 0; x < ds->patchWidth; x++ )
\r
552 s = (sBasis * x) + 0.5f;
\r
553 verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
\r
554 verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
\r
556 if( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ((ds->patchHeight - 1) * ds->patchWidth) + x ].xyz ) )
\r
557 lm->wrap[ 1 ] = qfalse;
\r
560 if( !VectorCompare( verts[ (y * ds->patchWidth) ].xyz, verts[ (y * ds->patchWidth) + (ds->patchWidth - 1) ].xyz ) )
\r
561 lm->wrap[ 0 ] = qfalse;
\r
565 //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
\r
566 //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
\r
567 //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
\r
568 //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
\r
569 //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
\r
571 /* add to counts */
\r
572 numPatchesLightmapped++;
\r
581 AddSurfaceToRawLightmap()
\r
582 projects a lightmap for a surface
\r
583 based on AllocateLightmapForSurface()
\r
586 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm )
\r
588 bspDrawSurface_t *ds, *ds2;
\r
589 surfaceInfo_t *info, *info2;
\r
590 int num2, n, i, axisNum;
\r
591 float s, t, d, len, sampleSize;
\r
592 vec3_t mins, maxs, origin, faxis, size, exactSize, delta, normalized, vecs[ 2 ];
\r
594 bspDrawVert_t *verts;
\r
597 /* get surface and info */
\r
598 ds = &bspDrawSurfaces[ num ];
\r
599 info = &surfaceInfos[ num ];
\r
601 /* add the surface to the raw lightmap */
\r
602 lightSurfaces[ numLightSurfaces++ ] = num;
\r
603 lm->numLightSurfaces++;
\r
605 /* does this raw lightmap already have any surfaces? */
\r
606 if( lm->numLightSurfaces > 1 )
\r
608 /* surface and raw lightmap must have the same lightmap projection axis */
\r
609 if( VectorCompare( info->axis, lm->axis ) == qfalse )
\r
612 /* match identical attributes */
\r
613 if( info->sampleSize != lm->sampleSize ||
\r
614 info->entityNum != lm->entityNum ||
\r
615 info->recvShadows != lm->recvShadows ||
\r
616 info->si->lmCustomWidth != lm->customWidth ||
\r
617 info->si->lmCustomHeight != lm->customHeight ||
\r
618 info->si->lmGamma != lm->gamma ||
\r
619 info->si->lmFilterRadius != lm->filterRadius ||
\r
620 info->si->splotchFix != lm->splotchFix )
\r
623 /* surface bounds must intersect with raw lightmap bounds */
\r
624 for( i = 0; i < 3; i++ )
\r
626 if( info->mins[ i ] > lm->maxs[ i ] )
\r
628 if( info->maxs[ i ] < lm->mins[ i ] )
\r
632 /* plane check (fixme: allow merging of nonplanars) */
\r
633 if( info->si->lmMergable == qfalse )
\r
635 if( info->plane == NULL || lm->plane == NULL )
\r
638 /* compare planes */
\r
639 for( i = 0; i < 4; i++ )
\r
640 if( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON )
\r
644 /* debug code hacking */
\r
645 //% if( lm->numLightSurfaces > 1 )
\r
650 if( info->plane == NULL )
\r
653 /* add surface to lightmap bounds */
\r
654 AddPointToBounds( info->mins, lm->mins, lm->maxs );
\r
655 AddPointToBounds( info->maxs, lm->mins, lm->maxs );
\r
657 /* check to see if this is a non-planar patch */
\r
658 if( ds->surfaceType == MST_PATCH &&
\r
659 lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f )
\r
660 return AddPatchToRawLightmap( num, lm );
\r
662 /* start with initially requested sample size */
\r
663 sampleSize = lm->sampleSize;
\r
665 /* round to the lightmap resolution */
\r
666 for( i = 0; i < 3; i++ )
\r
668 exactSize[ i ] = lm->maxs[ i ] - lm->mins[ i ];
\r
669 mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
\r
670 maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
\r
671 size[ i ] = (maxs[ i ] - mins[ i ]) / sampleSize + 1.0f;
\r
673 /* hack (god this sucks) */
\r
674 if( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight )
\r
677 sampleSize += 1.0f;
\r
681 /* set actual sample size */
\r
682 lm->actualSampleSize = sampleSize;
\r
684 /* fixme: copy rounded mins/maxes to lightmap record? */
\r
685 if( lm->plane == NULL )
\r
687 VectorCopy( mins, lm->mins );
\r
688 VectorCopy( maxs, lm->maxs );
\r
689 VectorCopy( mins, origin );
\r
692 /* set lightmap origin */
\r
693 VectorCopy( lm->mins, origin );
\r
695 /* make absolute axis */
\r
696 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
\r
697 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
\r
698 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
\r
700 /* clear out lightmap vectors */
\r
701 memset( vecs, 0, sizeof( vecs ) );
\r
703 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
\r
704 if( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] )
\r
709 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
\r
710 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
\r
712 else if( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] )
\r
717 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
\r
718 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
\r
725 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
\r
726 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
\r
729 /* check for bogus axis */
\r
730 if( faxis[ axisNum ] == 0.0f )
\r
732 Sys_Printf( "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
\r
737 /* store the axis number in the lightmap */
\r
738 lm->axisNum = axisNum;
\r
740 /* walk the list of surfaces on this raw lightmap */
\r
741 for( n = 0; n < lm->numLightSurfaces; n++ )
\r
744 num2 = lightSurfaces[ lm->firstLightSurface + n ];
\r
745 ds2 = &bspDrawSurfaces[ num2 ];
\r
746 info2 = &surfaceInfos[ num2 ];
\r
747 verts = &yDrawVerts[ ds2->firstVert ];
\r
749 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
\r
750 for( i = 0; i < ds2->numVerts; i++ )
\r
752 VectorSubtract( verts[ i ].xyz, origin, delta );
\r
753 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
\r
754 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
\r
755 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
\r
756 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
\r
758 if( s > (float) lm->w || t > (float) lm->h )
\r
760 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
\r
761 s, lm->w, t, lm->h );
\r
766 /* get first drawsurface */
\r
767 num2 = lightSurfaces[ lm->firstLightSurface ];
\r
768 ds2 = &bspDrawSurfaces[ num2 ];
\r
769 info2 = &surfaceInfos[ num2 ];
\r
770 verts = &yDrawVerts[ ds2->firstVert ];
\r
772 /* calculate lightmap origin */
\r
773 if( VectorLength( ds2->lightmapVecs[ 2 ] ) )
\r
774 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
\r
776 VectorCopy( lm->axis, plane );
\r
777 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
\r
779 VectorCopy( origin, lm->origin );
\r
780 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
\r
781 d /= plane[ axisNum ];
\r
782 lm->origin[ axisNum ] -= d;
\r
784 /* legacy support */
\r
785 VectorCopy( lm->origin, ds->lightmapOrigin );
\r
787 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
\r
788 if( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) /* ydnar: can't remember what exactly i was thinking here... */
\r
790 /* allocate space for the vectors */
\r
791 lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
\r
792 memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
\r
793 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
\r
795 /* project stepped lightmap blocks and subtract to get planevecs */
\r
796 for( i = 0; i < 2; i++ )
\r
798 len = VectorNormalize( vecs[ i ], normalized );
\r
799 VectorScale( normalized, (1.0 / len), lm->vecs[ i ] );
\r
800 d = DotProduct( lm->vecs[ i ], plane );
\r
801 d /= plane[ axisNum ];
\r
802 lm->vecs[ i ][ axisNum ] -= d;
\r
807 /* lightmap vectors are useless on a non-planar surface */
\r
811 /* add to counts */
\r
812 if( ds->surfaceType == MST_PATCH )
\r
814 numPatchesLightmapped++;
\r
815 if( lm->plane != NULL )
\r
816 numPlanarPatchesLightmapped++;
\r
820 if( lm->plane != NULL )
\r
821 numPlanarsLightmapped++;
\r
823 numNonPlanarsLightmapped++;
\r
833 CompareSurfaceInfo()
\r
834 compare function for qsort()
\r
837 static int CompareSurfaceInfo( const void *a, const void *b )
\r
839 surfaceInfo_t *aInfo, *bInfo;
\r
843 /* get surface info */
\r
844 aInfo = &surfaceInfos[ *((int*) a) ];
\r
845 bInfo = &surfaceInfos[ *((int*) b) ];
\r
848 if( aInfo->model < bInfo->model )
\r
850 else if( aInfo->model > bInfo->model )
\r
853 /* then lightmap status */
\r
854 if( aInfo->hasLightmap < bInfo->hasLightmap )
\r
856 else if( aInfo->hasLightmap > bInfo->hasLightmap )
\r
859 /* then lightmap sample size */
\r
860 if( aInfo->sampleSize < bInfo->sampleSize )
\r
862 else if( aInfo->sampleSize > bInfo->sampleSize )
\r
865 /* then lightmap axis */
\r
866 for( i = 0; i < 3; i++ )
\r
868 if( aInfo->axis[ i ] < bInfo->axis[ i ] )
\r
870 else if( aInfo->axis[ i ] > bInfo->axis[ i ] )
\r
875 if( aInfo->plane == NULL && bInfo->plane != NULL )
\r
877 else if( aInfo->plane != NULL && bInfo->plane == NULL )
\r
879 else if( aInfo->plane != NULL && bInfo->plane != NULL )
\r
881 for( i = 0; i < 4; i++ )
\r
883 if( aInfo->plane[ i ] < bInfo->plane[ i ] )
\r
885 else if( aInfo->plane[ i ] > bInfo->plane[ i ] )
\r
890 /* then position in world */
\r
891 for( i = 0; i < 3; i++ )
\r
893 if( aInfo->mins[ i ] < bInfo->mins[ i ] )
\r
895 else if( aInfo->mins[ i ] > bInfo->mins[ i ] )
\r
899 /* these are functionally identical (this should almost never happen) */
\r
906 SetupSurfaceLightmaps()
\r
907 allocates lightmaps for every surface in the bsp that needs one
\r
908 this depends on yDrawVerts being allocated
\r
911 void SetupSurfaceLightmaps( void )
\r
913 int i, j, k, s,num, num2;
\r
916 bspDrawSurface_t *ds, *ds2;
\r
917 surfaceInfo_t *info, *info2;
\r
920 vec3_t mapSize, entityOrigin;
\r
924 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n");
\r
926 /* determine supersample amount */
\r
927 if( superSample < 1 )
\r
929 else if( superSample > 8 )
\r
931 Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
\r
935 /* clear map bounds */
\r
936 ClearBounds( mapMins, mapMaxs );
\r
938 /* allocate a list of surface clusters */
\r
939 numSurfaceClusters = 0;
\r
940 maxSurfaceClusters = numBSPLeafSurfaces;
\r
941 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
\r
942 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
\r
944 /* allocate a list for per-surface info */
\r
945 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
\r
946 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
\r
947 for( i = 0; i < numBSPDrawSurfaces; i++ )
\r
948 surfaceInfos[ i ].childSurfaceNum = -1;
\r
950 /* allocate a list of surface indexes to be sorted */
\r
951 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
\r
952 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
\r
954 /* walk each model in the bsp */
\r
955 for( i = 0; i < numBSPModels; i++ )
\r
958 model = &bspModels[ i ];
\r
960 /* walk the list of surfaces in this model and fill out the info structs */
\r
961 for( j = 0; j < model->numBSPSurfaces; j++ )
\r
963 /* make surface index */
\r
964 num = model->firstBSPSurface + j;
\r
966 /* copy index to sort list */
\r
967 sortSurfaces[ num ] = num;
\r
969 /* get surface and info */
\r
970 ds = &bspDrawSurfaces[ num ];
\r
971 info = &surfaceInfos[ num ];
\r
973 /* set entity origin */
\r
974 if( ds->numVerts > 0 )
\r
975 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
\r
977 VectorClear( entityOrigin );
\r
980 info->model = model;
\r
982 info->plane = NULL;
\r
983 info->firstSurfaceCluster = numSurfaceClusters;
\r
985 /* get extra data */
\r
986 info->si = GetSurfaceExtraShaderInfo( num );
\r
987 if( info->si == NULL )
\r
988 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
\r
989 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
\r
990 info->entityNum = GetSurfaceExtraEntityNum( num );
\r
991 info->castShadows = GetSurfaceExtraCastShadows( num );
\r
992 info->recvShadows = GetSurfaceExtraRecvShadows( num );
\r
993 info->sampleSize = GetSurfaceExtraSampleSize( num );
\r
994 info->longestCurve = GetSurfaceExtraLongestCurve( num );
\r
995 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
\r
996 GetSurfaceExtraLightmapAxis( num, info->axis );
\r
999 if( info->parentSurfaceNum >= 0 )
\r
1000 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
\r
1002 /* determine surface bounds */
\r
1003 ClearBounds( info->mins, info->maxs );
\r
1004 for( k = 0; k < ds->numVerts; k++ )
\r
1006 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
\r
1007 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
\r
1010 /* find all the bsp clusters the surface falls into */
\r
1011 for( k = 0; k < numBSPLeafs; k++ )
\r
1014 leaf = &bspLeafs[ k ];
\r
1017 if( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
\r
1018 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
\r
1019 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] )
\r
1022 /* test leaf surfaces */
\r
1023 for( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
\r
1025 if( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num )
\r
1027 if( numSurfaceClusters >= maxSurfaceClusters )
\r
1028 Error( "maxSurfaceClusters exceeded" );
\r
1029 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
\r
1030 numSurfaceClusters++;
\r
1031 info->numSurfaceClusters++;
\r
1036 /* determine if surface is planar */
\r
1037 if( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f )
\r
1039 /* make a plane */
\r
1040 info->plane = safe_malloc( 4 * sizeof( float ) );
\r
1041 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
\r
1042 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
\r
1045 /* determine if surface requires a lightmap */
\r
1046 if( ds->surfaceType == MST_TRIANGLE_SOUP ||
\r
1047 ds->surfaceType == MST_FOLIAGE ||
\r
1048 (info->si->compileFlags & C_VERTEXLIT) )
\r
1049 numSurfsVertexLit++;
\r
1052 numSurfsLightmapped++;
\r
1053 info->hasLightmap = qtrue;
\r
1058 /* find longest map distance */
\r
1059 VectorSubtract( mapMaxs, mapMins, mapSize );
\r
1060 maxMapDistance = VectorLength( mapSize );
\r
1062 /* sort the surfaces info list */
\r
1063 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
\r
1065 /* allocate a list of surfaces that would go into raw lightmaps */
\r
1066 numLightSurfaces = 0;
\r
1067 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
\r
1068 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
\r
1070 /* allocate a list of raw lightmaps */
\r
1071 numRawSuperLuxels = 0;
\r
1072 numRawLightmaps = 0;
\r
1073 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
\r
1074 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
\r
1076 /* walk the list of sorted surfaces */
\r
1077 for( i = 0; i < numBSPDrawSurfaces; i++ )
\r
1079 /* get info and attempt early out */
\r
1080 num = sortSurfaces[ i ];
\r
1081 ds = &bspDrawSurfaces[ num ];
\r
1082 info = &surfaceInfos[ num ];
\r
1083 if( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 )
\r
1086 /* allocate a new raw lightmap */
\r
1087 lm = &rawLightmaps[ numRawLightmaps ];
\r
1088 numRawLightmaps++;
\r
1091 lm->splotchFix = info->si->splotchFix;
\r
1092 lm->firstLightSurface = numLightSurfaces;
\r
1093 lm->numLightSurfaces = 0;
\r
1094 lm->sampleSize = info->sampleSize;
\r
1095 lm->actualSampleSize = info->sampleSize;
\r
1096 lm->entityNum = info->entityNum;
\r
1097 lm->recvShadows = info->recvShadows;
\r
1098 lm->gamma = info->si->lmGamma;
\r
1099 lm->filterRadius = info->si->lmFilterRadius;
\r
1100 VectorCopy( info->axis, lm->axis );
\r
1101 lm->plane = info->plane;
\r
1102 VectorCopy( info->mins, lm->mins );
\r
1103 VectorCopy( info->maxs, lm->maxs );
\r
1105 lm->customWidth = info->si->lmCustomWidth;
\r
1106 lm->customHeight = info->si->lmCustomHeight;
\r
1108 /* add the surface to the raw lightmap */
\r
1109 AddSurfaceToRawLightmap( num, lm );
\r
1112 /* do an exhaustive merge */
\r
1116 /* walk the list of surfaces again */
\r
1118 for( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
\r
1120 /* get info and attempt early out */
\r
1121 num2 = sortSurfaces[ j ];
\r
1122 ds2 = &bspDrawSurfaces[ num2 ];
\r
1123 info2 = &surfaceInfos[ num2 ];
\r
1124 if( info2->hasLightmap == qfalse || info2->lm != NULL )
\r
1127 /* add the surface to the raw lightmap */
\r
1128 if( AddSurfaceToRawLightmap( num2, lm ) )
\r
1136 lm->numLightSurfaces--;
\r
1137 numLightSurfaces--;
\r
1142 /* finish the lightmap and allocate the various buffers */
\r
1143 FinishRawLightmap( lm );
\r
1146 /* allocate vertex luxel storage */
\r
1147 for( k = 0; k < MAX_LIGHTMAPS; k++ )
\r
1149 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
\r
1150 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
\r
1151 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
\r
1152 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
\r
1155 /* emit some stats */
\r
1156 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
\r
1157 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
\r
1158 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
\r
1159 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
\r
1160 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
\r
1161 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
\r
1162 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
\r
1163 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
\r
1169 StitchSurfaceLightmaps()
\r
1170 stitches lightmap edges
\r
1171 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
\r
1174 #define MAX_STITCH_CANDIDATES 32
\r
1175 #define MAX_STITCH_LUXELS 64
\r
1177 void StitchSurfaceLightmaps( void )
\r
1179 int i, j, x, y, x2, y2, *cluster, *cluster2,
\r
1180 numStitched, numCandidates, numLuxels, f, fOld, start;
\r
1181 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
\r
1182 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
\r
1183 sampleSize, average[ 3 ], totalColor, ootc, *luxels[ MAX_STITCH_LUXELS ];
\r
1186 /* disabled for now */
\r
1190 Sys_Printf( "--- StitchSurfaceLightmaps ---\n");
\r
1192 /* init pacifier */
\r
1194 start = I_FloatTime();
\r
1196 /* walk the list of raw lightmaps */
\r
1198 for( i = 0; i < numRawLightmaps; i++ )
\r
1200 /* print pacifier */
\r
1201 f = 10 * i / numRawLightmaps;
\r
1205 Sys_Printf( "%i...", f );
\r
1208 /* get lightmap a */
\r
1209 a = &rawLightmaps[ i ];
\r
1211 /* walk rest of lightmaps */
\r
1212 numCandidates = 0;
\r
1213 for( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
\r
1215 /* get lightmap b */
\r
1216 b = &rawLightmaps[ j ];
\r
1218 /* test bounding box */
\r
1219 if( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
\r
1220 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
\r
1221 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] )
\r
1224 /* add candidate */
\r
1225 c[ numCandidates++ ] = b;
\r
1229 for( y = 0; y < a->sh; y++ )
\r
1231 for( x = 0; x < a->sw; x++ )
\r
1233 /* ignore unmapped/unlit luxels */
\r
1235 cluster = SUPER_CLUSTER( x, y );
\r
1236 if( *cluster == CLUSTER_UNMAPPED )
\r
1238 luxel = SUPER_LUXEL( 0, x, y );
\r
1239 if( luxel[ 3 ] <= 0.0f )
\r
1242 /* get particulars */
\r
1243 origin = SUPER_ORIGIN( x, y );
\r
1244 normal = SUPER_NORMAL( x, y );
\r
1246 /* walk candidate list */
\r
1247 for( j = 0; j < numCandidates; j++ )
\r
1249 /* get candidate */
\r
1253 /* set samplesize to the smaller of the pair */
\r
1254 sampleSize = 0.5f * (a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize);
\r
1256 /* test bounding box */
\r
1257 if( origin[ 0 ] < (b->mins[ 0 ] - sampleSize) || (origin[ 0 ] > b->maxs[ 0 ] + sampleSize) ||
\r
1258 origin[ 1 ] < (b->mins[ 1 ] - sampleSize) || (origin[ 1 ] > b->maxs[ 1 ] + sampleSize) ||
\r
1259 origin[ 2 ] < (b->mins[ 2 ] - sampleSize) || (origin[ 2 ] > b->maxs[ 2 ] + sampleSize) )
\r
1262 /* walk candidate luxels */
\r
1263 VectorClear( average );
\r
1265 totalColor = 0.0f;
\r
1266 for( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
\r
1268 for( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
\r
1270 /* ignore same luxels */
\r
1271 if( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 )
\r
1274 /* ignore unmapped/unlit luxels */
\r
1275 cluster2 = SUPER_CLUSTER( x2, y2 );
\r
1276 if( *cluster2 == CLUSTER_UNMAPPED )
\r
1278 luxel2 = SUPER_LUXEL( 0, x2, y2 );
\r
1279 if( luxel2[ 3 ] <= 0.0f )
\r
1282 /* get particulars */
\r
1283 origin2 = SUPER_ORIGIN( x2, y2 );
\r
1284 normal2 = SUPER_NORMAL( x2, y2 );
\r
1287 if( DotProduct( normal, normal2 ) < 0.5f )
\r
1291 if( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
\r
1292 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
\r
1293 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize )
\r
1297 //% VectorSet( luxel2, 255, 0, 255 );
\r
1298 luxels[ numLuxels++ ] = luxel2;
\r
1299 VectorAdd( average, luxel2, average );
\r
1300 totalColor += luxel2[ 3 ];
\r
1305 if( numLuxels == 0 )
\r
1308 /* scale average */
\r
1309 ootc = 1.0f / totalColor;
\r
1310 VectorScale( average, ootc, luxel );
\r
1311 luxel[ 3 ] = 1.0f;
\r
1318 /* emit statistics */
\r
1319 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
\r
1320 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
\r
1326 CompareBSPLuxels()
\r
1327 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
\r
1330 #define LUXEL_TOLERANCE 0.0025
\r
1331 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
\r
1333 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
\r
1335 rawLightmap_t *lm;
\r
1337 double delta, total, rd, gd, bd;
\r
1338 float *aLuxel, *bLuxel;
\r
1341 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
\r
1342 if( (minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ]) &&
\r
1343 ((aNum == 0 && bNum != 0) || (aNum != 0 && bNum == 0)) )
\r
1347 if( a->w != b->w || a->h != b->h ||
\r
1348 a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
\r
1349 a->gamma != b->gamma ||
\r
1350 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
\r
1353 /* compare luxels */
\r
1356 for( y = 0; y < a->h; y++ )
\r
1358 for( x = 0; x < a->w; x++ )
\r
1360 /* increment total */
\r
1364 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
\r
1365 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
\r
1367 /* ignore unused luxels */
\r
1368 if( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 )
\r
1372 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
\r
1373 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
\r
1374 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
\r
1376 /* 2003-09-27: compare individual luxels */
\r
1377 if( rd > 3.0 || gd > 3.0 || bd > 3.0 )
\r
1380 /* compare (fixme: take into account perceptual differences) */
\r
1381 delta += rd * LUXEL_COLOR_FRAC;
\r
1382 delta += gd * LUXEL_COLOR_FRAC;
\r
1383 delta += bd * LUXEL_COLOR_FRAC;
\r
1385 /* is the change too high? */
\r
1386 if( total > 0.0 && ((delta / total) > LUXEL_TOLERANCE) )
\r
1391 /* made it this far, they must be identical (or close enough) */
\r
1399 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
\r
1402 static void MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
\r
1404 rawLightmap_t *lm;
\r
1406 float luxel[ 3 ], *aLuxel, *bLuxel;
\r
1410 if( a->w != b->w || a->h != b->h ||
\r
1411 a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
\r
1412 a->gamma != b->gamma ||
\r
1413 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
\r
1416 /* merge luxels */
\r
1417 for( y = 0; y < a->h; y++ )
\r
1419 for( x = 0; x < a->w; x++ )
\r
1422 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
\r
1423 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
\r
1425 /* handle occlusion mismatch */
\r
1426 if( aLuxel[ 0 ] < 0.0f )
\r
1427 VectorCopy( bLuxel, aLuxel );
\r
1428 else if( bLuxel[ 0 ] < 0.0f )
\r
1429 VectorCopy( aLuxel, bLuxel );
\r
1433 VectorAdd( aLuxel, bLuxel, luxel );
\r
1434 VectorScale( luxel, 0.5f, luxel );
\r
1436 /* debugging code */
\r
1437 //% luxel[ 2 ] += 64.0f;
\r
1439 /* copy to both */
\r
1440 VectorCopy( luxel, aLuxel );
\r
1441 VectorCopy( luxel, bLuxel );
\r
1450 ApproximateLuxel()
\r
1451 determines if a single luxel is can be approximated with the interpolated vertex rgba
\r
1454 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv )
\r
1456 int i, x, y, d, lightmapNum;
\r
1458 vec3_t color, vertexColor;
\r
1459 byte cb[ 4 ], vcb[ 4 ];
\r
1462 /* find luxel xy coords */
\r
1463 x = dv->lightmap[ 0 ][ 0 ] / superSample;
\r
1464 y = dv->lightmap[ 0 ][ 1 ] / superSample;
\r
1467 else if( x >= lm->w )
\r
1471 else if( y >= lm->h )
\r
1475 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
1478 if( lm->styles[ lightmapNum ] == LS_NONE )
\r
1482 luxel = BSP_LUXEL( lightmapNum, x, y );
\r
1484 /* ignore occluded luxels */
\r
1485 if( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f )
\r
1488 /* copy, set min color and compare */
\r
1489 VectorCopy( luxel, color );
\r
1490 VectorCopy( dv->color[ 0 ], vertexColor );
\r
1492 /* styles are not affected by minlight */
\r
1493 if( lightmapNum == 0 )
\r
1495 for( i = 0; i < 3; i++ )
\r
1497 /* set min color */
\r
1498 if( color[ i ] < minLight[ i ] )
\r
1499 color[ i ] = minLight[ i ];
\r
1500 if( vertexColor[ i ] < minLight[ i ] ) /* note NOT minVertexLight */
\r
1501 vertexColor[ i ] = minLight[ i ];
\r
1505 /* set to bytes */
\r
1506 ColorToBytes( color, cb, 1.0f );
\r
1507 ColorToBytes( vertexColor, vcb, 1.0f );
\r
1510 for( i = 0; i < 3; i++ )
\r
1512 d = cb[ i ] - vcb[ i ];
\r
1515 if( d > approximateTolerance )
\r
1520 /* close enough for the girls i date */
\r
1527 ApproximateTriangle()
\r
1528 determines if a single triangle can be approximated with vertex rgba
\r
1531 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] )
\r
1533 bspDrawVert_t mid, *dv2[ 3 ];
\r
1537 /* approximate the vertexes */
\r
1538 if( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse )
\r
1540 if( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse )
\r
1542 if( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse )
\r
1545 /* subdivide calc */
\r
1548 float dx, dy, dist, maxDist;
\r
1551 /* find the longest edge and split it */
\r
1554 for( i = 0; i < 3; i++ )
\r
1556 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 0 ];
\r
1557 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 1 ];
\r
1558 dist = sqrt( (dx * dx) + (dy * dy) );
\r
1559 if( dist > maxDist )
\r
1566 /* try to early out */
\r
1567 if( i < 0 || maxDist < subdivideThreshold )
\r
1571 /* split the longest edge and map it */
\r
1572 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
\r
1573 if( ApproximateLuxel( lm, &mid ) == qfalse )
\r
1576 /* recurse to first triangle */
\r
1577 VectorCopy( dv, dv2 );
\r
1578 dv2[ max ] = ∣
\r
1579 if( ApproximateTriangle_r( lm, dv2 ) == qfalse )
\r
1582 /* recurse to second triangle */
\r
1583 VectorCopy( dv, dv2 );
\r
1584 dv2[ (max + 1) % 3 ] = ∣
\r
1585 return ApproximateTriangle_r( lm, dv2 );
\r
1591 ApproximateLightmap()
\r
1592 determines if a raw lightmap can be approximated sufficiently with vertex colors
\r
1595 static qboolean ApproximateLightmap( rawLightmap_t *lm )
\r
1597 int n, num, i, x, y, pw[ 5 ], r;
\r
1598 bspDrawSurface_t *ds;
\r
1599 surfaceInfo_t *info;
\r
1600 mesh_t src, *subdivided, *mesh;
\r
1601 bspDrawVert_t *verts, *dv[ 3 ];
\r
1602 qboolean approximated;
\r
1605 /* approximating? */
\r
1606 if( approximateTolerance <= 0 )
\r
1609 /* test for jmonroe */
\r
1611 /* don't approx lightmaps with styled twins */
\r
1612 if( lm->numStyledTwins > 0 )
\r
1615 /* don't approx lightmaps with styles */
\r
1616 for( i = 1; i < MAX_LIGHTMAPS; i++ )
\r
1618 if( lm->styles[ i ] != LS_NONE )
\r
1623 /* assume reduced until shadow detail is found */
\r
1624 approximated = qtrue;
\r
1626 /* walk the list of surfaces on this raw lightmap */
\r
1627 for( n = 0; n < lm->numLightSurfaces; n++ )
\r
1630 num = lightSurfaces[ lm->firstLightSurface + n ];
\r
1631 ds = &bspDrawSurfaces[ num ];
\r
1632 info = &surfaceInfos[ num ];
\r
1634 /* bail if lightmap doesn't match up */
\r
1635 if( info->lm != lm )
\r
1638 /* assume reduced initially */
\r
1639 info->approximated = qtrue;
\r
1641 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
\r
1642 if( (info->maxs[ 0 ] - info->mins[ 0 ]) <= (2.0f * info->sampleSize) &&
\r
1643 (info->maxs[ 1 ] - info->mins[ 1 ]) <= (2.0f * info->sampleSize) &&
\r
1644 (info->maxs[ 2 ] - info->mins[ 2 ]) <= (2.0f * info->sampleSize) )
\r
1646 numSurfsVertexForced++;
\r
1650 /* handle the triangles */
\r
1651 switch( ds->surfaceType )
\r
1655 verts = yDrawVerts + ds->firstVert;
\r
1657 /* map the triangles */
\r
1658 for( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
\r
1660 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
\r
1661 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
\r
1662 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
\r
1663 info->approximated = ApproximateTriangle_r( lm, dv );
\r
1668 /* make a mesh from the drawsurf */
\r
1669 src.width = ds->patchWidth;
\r
1670 src.height = ds->patchHeight;
\r
1671 src.verts = &yDrawVerts[ ds->firstVert ];
\r
1672 //% subdivided = SubdivideMesh( src, 8, 512 );
\r
1673 subdivided = SubdivideMesh2( src, info->patchIterations );
\r
1675 /* fit it to the curve and remove colinear verts on rows/columns */
\r
1676 PutMeshOnCurve( *subdivided );
\r
1677 mesh = RemoveLinearMeshColumnsRows( subdivided );
\r
1678 FreeMesh( subdivided );
\r
1681 verts = mesh->verts;
\r
1683 /* map the mesh quads */
\r
1684 for( y = 0; y < (mesh->height - 1) && info->approximated; y++ )
\r
1686 for( x = 0; x < (mesh->width - 1) && info->approximated; x++ )
\r
1689 pw[ 0 ] = x + (y * mesh->width);
\r
1690 pw[ 1 ] = x + ((y + 1) * mesh->width);
\r
1691 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
\r
1692 pw[ 3 ] = x + 1 + (y * mesh->width);
\r
1693 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
\r
1698 /* get drawverts and map first triangle */
\r
1699 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
\r
1700 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
\r
1701 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
\r
1702 info->approximated = ApproximateTriangle_r( lm, dv );
\r
1704 /* get drawverts and map second triangle */
\r
1705 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
\r
1706 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
\r
1707 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
\r
1708 if( info->approximated )
\r
1709 info->approximated = ApproximateTriangle_r( lm, dv );
\r
1713 /* free the mesh */
\r
1722 if( info->approximated == qfalse )
\r
1723 approximated = qfalse;
\r
1725 numSurfsVertexApproximated++;
\r
1729 return approximated;
\r
1735 TestOutLightmapStamp()
\r
1736 tests a stamp on a given lightmap for validity
\r
1739 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y )
\r
1741 int sx, sy, ox, oy, offset;
\r
1745 /* bounds check */
\r
1746 if( x < 0 || y < 0 || (x + lm->w) > olm->customWidth || (y + lm->h) > olm->customHeight )
\r
1749 /* test the stamp */
\r
1750 for( sy = 0; sy < lm->h; sy++ )
\r
1752 for( sx = 0; sx < lm->w; sx++ )
\r
1755 luxel = BSP_LUXEL( lightmapNum, sx, sy );
\r
1756 if( luxel[ 0 ] < 0.0f )
\r
1759 /* get bsp lightmap coords and test */
\r
1762 offset = (oy * olm->customWidth) + ox;
\r
1763 if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
\r
1768 /* stamp is empty */
\r
1775 SetupOutLightmap()
\r
1776 sets up an output lightmap
\r
1779 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm )
\r
1782 if( lm == NULL || olm == NULL )
\r
1785 /* is this a "normal" bsp-stored lightmap? */
\r
1786 if( (lm->customWidth == LIGHTMAP_WIDTH && lm->customHeight == LIGHTMAP_HEIGHT) || externalLightmaps )
\r
1788 olm->lightmapNum = numBSPLightmaps;
\r
1789 numBSPLightmaps++;
\r
1791 /* lightmaps are interleaved with light direction maps */
\r
1793 numBSPLightmaps++;
\r
1796 olm->lightmapNum = -3;
\r
1798 /* set external lightmap number */
\r
1799 olm->extLightmapNum = -1;
\r
1802 olm->numLightmaps = 0;
\r
1803 olm->customWidth = lm->customWidth;
\r
1804 olm->customHeight = lm->customHeight;
\r
1805 olm->freeLuxels = olm->customWidth * olm->customHeight;
\r
1806 olm->numShaders = 0;
\r
1808 /* allocate buffers */
\r
1809 olm->lightBits = safe_malloc( (olm->customWidth * olm->customHeight / 8) + 8 );
\r
1810 memset( olm->lightBits, 0, (olm->customWidth * olm->customHeight / 8) + 8 );
\r
1811 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
\r
1812 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
\r
1815 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
\r
1816 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
\r
1823 FindOutLightmaps()
\r
1824 for a given surface lightmap, find output lightmap pages and positions for it
\r
1827 static void FindOutLightmaps( rawLightmap_t *lm )
\r
1829 int i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
\r
1830 outLightmap_t *olm;
\r
1831 surfaceInfo_t *info;
\r
1832 float *luxel, *deluxel;
\r
1833 vec3_t color, direction;
\r
1838 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
\r
1839 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
1840 lm->outLightmapNums[ lightmapNum ] = -3;
\r
1842 /* can this lightmap be approximated with vertex color? */
\r
1843 if( ApproximateLightmap( lm ) )
\r
1847 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
1850 if( lm->styles[ lightmapNum ] == LS_NONE )
\r
1853 /* don't store twinned lightmaps */
\r
1854 if( lm->twins[ lightmapNum ] != NULL )
\r
1857 /* if this is a styled lightmap, try some normalized locations first */
\r
1859 if( lightmapNum > 0 && outLightmaps != NULL )
\r
1862 for( j = 0; j < 2; j++ )
\r
1864 /* try identical position */
\r
1865 for( i = 0; i < numOutLightmaps; i++ )
\r
1867 /* get the output lightmap */
\r
1868 olm = &outLightmaps[ i ];
\r
1870 /* simple early out test */
\r
1871 if( olm->freeLuxels < lm->used )
\r
1874 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
\r
1875 if( olm->customWidth != lm->customWidth ||
\r
1876 olm->customHeight != lm->customHeight )
\r
1879 /* try identical */
\r
1882 x = lm->lightmapX[ 0 ];
\r
1883 y = lm->lightmapY[ 0 ];
\r
1884 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
\r
1887 /* try shifting */
\r
1890 for( sy = -1; sy <= 1; sy++ )
\r
1892 for( sx = -1; sx <= 1; sx++ )
\r
1894 x = lm->lightmapX[ 0 ] + sx * (olm->customWidth >> 1); //% lm->w;
\r
1895 y = lm->lightmapY[ 0 ] + sy * (olm->customHeight >> 1); //% lm->h;
\r
1896 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
\r
1916 /* try normal placement algorithm */
\r
1917 if( ok == qfalse )
\r
1919 /* reset origin */
\r
1923 /* walk the list of lightmap pages */
\r
1924 for( i = 0; i < numOutLightmaps; i++ )
\r
1926 /* get the output lightmap */
\r
1927 olm = &outLightmaps[ i ];
\r
1929 /* simple early out test */
\r
1930 if( olm->freeLuxels < lm->used )
\r
1933 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
\r
1934 if( olm->customWidth != lm->customWidth ||
\r
1935 olm->customHeight != lm->customHeight )
\r
1939 xMax = (olm->customWidth - lm->w) + 1;
\r
1940 yMax = (olm->customHeight - lm->h) + 1;
\r
1942 /* walk the origin around the lightmap */
\r
1943 for( y = 0; y < yMax; y++ )
\r
1945 for( x = 0; x < xMax; x++ )
\r
1947 /* find a fine tract of lauhnd */
\r
1948 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
\r
1961 /* reset x and y */
\r
1968 if( ok == qfalse )
\r
1970 /* allocate two new output lightmaps */
\r
1971 numOutLightmaps += 2;
\r
1972 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
\r
1973 if( outLightmaps != NULL && numOutLightmaps > 2 )
\r
1975 memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );
\r
1976 free( outLightmaps );
\r
1978 outLightmaps = olm;
\r
1980 /* initialize both out lightmaps */
\r
1981 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
\r
1982 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
\r
1984 /* set out lightmap */
\r
1985 i = numOutLightmaps - 2;
\r
1986 olm = &outLightmaps[ i ];
\r
1988 /* set stamp xy origin to the first surface lightmap */
\r
1989 if( lightmapNum > 0 )
\r
1991 x = lm->lightmapX[ 0 ];
\r
1992 y = lm->lightmapY[ 0 ];
\r
1996 /* if this is a style-using lightmap, it must be exported */
\r
1997 if( lightmapNum > 0 )
\r
1998 olm->extLightmapNum = 0;
\r
2000 /* add the surface lightmap to the bsp lightmap */
\r
2001 lm->outLightmapNums[ lightmapNum ] = i;
\r
2002 lm->lightmapX[ lightmapNum ] = x;
\r
2003 lm->lightmapY[ lightmapNum ] = y;
\r
2004 olm->numLightmaps++;
\r
2007 for( i = 0; i < lm->numLightSurfaces; i++ )
\r
2009 /* get surface info */
\r
2010 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
\r
2012 /* test for shader */
\r
2013 for( j = 0; j < olm->numShaders; j++ )
\r
2015 if( olm->shaders[ j ] == info->si )
\r
2019 /* if it doesn't exist, add it */
\r
2020 if( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS )
\r
2022 olm->shaders[ olm->numShaders ] = info->si;
\r
2023 olm->numShaders++;
\r
2024 numLightmapShaders++;
\r
2028 /* mark the bits used */
\r
2029 for( y = 0; y < lm->h; y++ )
\r
2031 for( x = 0; x < lm->w; x++ )
\r
2034 luxel = BSP_LUXEL( lightmapNum, x, y );
\r
2035 deluxel = BSP_DELUXEL( x, y );
\r
2036 if( luxel[ 0 ] < 0.0f )
\r
2039 /* set minimum light */
\r
2040 VectorCopy( luxel, color );
\r
2042 /* styles are not affected by minlight */
\r
2043 if( lightmapNum == 0 )
\r
2045 for( i = 0; i < 3; i++ )
\r
2047 if( color[ i ] < minLight[ i ] )
\r
2048 color[ i ] = minLight[ i ];
\r
2052 /* get bsp lightmap coords */
\r
2053 ox = x + lm->lightmapX[ lightmapNum ];
\r
2054 oy = y + lm->lightmapY[ lightmapNum ];
\r
2055 offset = (oy * olm->customWidth) + ox;
\r
2057 /* flag pixel as used */
\r
2058 olm->lightBits[ offset >> 3 ] |= (1 << (offset & 7));
\r
2059 olm->freeLuxels--;
\r
2062 pixel = olm->bspLightBytes + (((oy * olm->customWidth) + ox) * 3);
\r
2063 ColorToBytes( color, pixel, lm->gamma );
\r
2065 /* store direction */
\r
2068 /* normalize average light direction */
\r
2069 if( VectorNormalize( deluxel, direction ) )
\r
2071 /* encode [-1,1] in [0,255] */
\r
2072 pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
\r
2073 for( i = 0; i < 3; i++ )
\r
2075 temp = (direction[ i ] + 1.0f) * 127.5f;
\r
2078 else if( temp > 255 )
\r
2081 pixel[ i ] = temp;
\r
2093 CompareRawLightmap()
\r
2094 compare function for qsort()
\r
2097 static int CompareRawLightmap( const void *a, const void *b )
\r
2099 rawLightmap_t *alm, *blm;
\r
2100 surfaceInfo_t *aInfo, *bInfo;
\r
2104 /* get lightmaps */
\r
2105 alm = &rawLightmaps[ *((int*) a) ];
\r
2106 blm = &rawLightmaps[ *((int*) b) ];
\r
2108 /* get min number of surfaces */
\r
2109 min = (alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces);
\r
2112 for( i = 0; i < min; i++ )
\r
2114 /* get surface info */
\r
2115 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
\r
2116 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
\r
2118 /* compare shader names */
\r
2119 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
\r
2124 /* test style count */
\r
2126 for( i = 0; i < MAX_LIGHTMAPS; i++ )
\r
2127 diff += blm->styles[ i ] - alm->styles[ i ];
\r
2131 /* compare size */
\r
2132 diff = (blm->w * blm->h) - (alm->w * alm->h);
\r
2136 /* must be equivalent */
\r
2143 StoreSurfaceLightmaps()
\r
2144 stores the surface lightmaps into the bsp as byte rgb triplets
\r
2147 void StoreSurfaceLightmaps( void )
\r
2149 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
\r
2150 int style, size, lightmapNum, lightmapNum2;
\r
2151 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
\r
2152 vec3_t sample, occludedSample, dirSample;
\r
2153 float *deluxel, *bspDeluxel, *bspDeluxel2;
\r
2155 int numUsed, numTwins, numTwinLuxels, numStored;
\r
2156 float lmx, lmy, efficiency;
\r
2158 bspDrawSurface_t *ds, *parent, dsTemp;
\r
2159 surfaceInfo_t *info;
\r
2160 rawLightmap_t *lm, *lm2;
\r
2161 outLightmap_t *olm;
\r
2162 bspDrawVert_t *dv, *ydv, *dvParent;
\r
2163 char dirname[ 1024 ], filename[ 1024 ];
\r
2164 shaderInfo_t *csi;
\r
2165 char lightmapName[ 128 ];
\r
2166 char *rgbGenValues[ 256 ];
\r
2167 char *alphaGenValues[ 256 ];
\r
2171 Sys_Printf( "--- StoreSurfaceLightmaps ---\n");
\r
2174 strcpy( dirname, source );
\r
2175 StripExtension( dirname );
\r
2176 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
\r
2177 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
\r
2179 /* -----------------------------------------------------------------
\r
2180 average the sampled luxels into the bsp luxels
\r
2181 ----------------------------------------------------------------- */
\r
2184 Sys_FPrintf( SYS_VRB, "Subsampling..." );
\r
2186 /* walk the list of raw lightmaps */
\r
2189 numTwinLuxels = 0;
\r
2190 for( i = 0; i < numRawLightmaps; i++ )
\r
2192 /* get lightmap */
\r
2193 lm = &rawLightmaps[ i ];
\r
2195 /* walk individual lightmaps */
\r
2196 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2199 if( lm->superLuxels[ lightmapNum ] == NULL )
\r
2202 /* allocate bsp luxel storage */
\r
2203 if( lm->bspLuxels[ lightmapNum ] == NULL )
\r
2205 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
\r
2206 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
\r
2207 memset( lm->bspLuxels[ lightmapNum ], 0, size );
\r
2210 /* allocate radiosity lightmap storage */
\r
2213 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
\r
2214 if( lm->radLuxels[ lightmapNum ] == NULL )
\r
2215 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
\r
2216 memset( lm->radLuxels[ lightmapNum ], 0, size );
\r
2219 /* average supersampled luxels */
\r
2220 for( y = 0; y < lm->h; y++ )
\r
2222 for( x = 0; x < lm->w; x++ )
\r
2226 occludedSamples = 0.0f;
\r
2227 mappedSamples = 0;
\r
2228 VectorClear( sample );
\r
2229 VectorClear( occludedSample );
\r
2230 VectorClear( dirSample );
\r
2231 for( ly = 0; ly < superSample; ly++ )
\r
2233 for( lx = 0; lx < superSample; lx++ )
\r
2235 /* sample luxel */
\r
2236 sx = x * superSample + lx;
\r
2237 sy = y * superSample + ly;
\r
2238 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
\r
2239 deluxel = SUPER_DELUXEL( sx, sy );
\r
2240 normal = SUPER_NORMAL( sx, sy );
\r
2241 cluster = SUPER_CLUSTER( sx, sy );
\r
2243 /* sample deluxemap */
\r
2244 if( deluxemap && lightmapNum == 0 )
\r
2245 VectorAdd( dirSample, deluxel, dirSample );
\r
2247 /* keep track of used/occluded samples */
\r
2248 if( *cluster != CLUSTER_UNMAPPED )
\r
2251 /* handle lightmap border? */
\r
2252 if( lightmapBorder && (sx == 0 || sx == (lm->sw - 1) || sy == 0 || sy == (lm->sh - 1) ) && luxel[ 3 ] > 0.0f )
\r
2254 VectorSet( sample, 255.0f, 0.0f, 0.0f );
\r
2258 /* handle debug */
\r
2259 else if( debug && *cluster < 0 )
\r
2261 if( *cluster == CLUSTER_UNMAPPED )
\r
2262 VectorSet( luxel, 255, 204, 0 );
\r
2263 else if( *cluster == CLUSTER_OCCLUDED )
\r
2264 VectorSet( luxel, 255, 0, 255 );
\r
2265 else if( *cluster == CLUSTER_FLOODED )
\r
2266 VectorSet( luxel, 0, 32, 255 );
\r
2267 VectorAdd( occludedSample, luxel, occludedSample );
\r
2268 occludedSamples += 1.0f;
\r
2271 /* normal luxel handling */
\r
2272 else if( luxel[ 3 ] > 0.0f )
\r
2274 /* handle lit or flooded luxels */
\r
2275 if( *cluster > 0 || *cluster == CLUSTER_FLOODED )
\r
2277 VectorAdd( sample, luxel, sample );
\r
2278 samples += luxel[ 3 ];
\r
2281 /* handle occluded or unmapped luxels */
\r
2284 VectorAdd( occludedSample, luxel, occludedSample );
\r
2285 occludedSamples += luxel[ 3 ];
\r
2288 /* handle style debugging */
\r
2289 if( debug && lightmapNum > 0 && x < 2 && y < 2 )
\r
2291 VectorCopy( debugColors[ 0 ], sample );
\r
2298 /* only use occluded samples if necessary */
\r
2299 if( samples <= 0.0f )
\r
2301 VectorCopy( occludedSample, sample );
\r
2302 samples = occludedSamples;
\r
2306 luxel = SUPER_LUXEL( lightmapNum, x, y );
\r
2307 deluxel = SUPER_DELUXEL( x, y );
\r
2309 /* store light direction */
\r
2310 if( deluxemap && lightmapNum == 0 )
\r
2311 VectorCopy( dirSample, deluxel );
\r
2313 /* store the sample back in super luxels */
\r
2314 if( samples > 0.01f )
\r
2316 VectorScale( sample, (1.0f / samples), luxel );
\r
2317 luxel[ 3 ] = 1.0f;
\r
2320 /* if any samples were mapped in any way, store ambient color */
\r
2321 else if( mappedSamples > 0 )
\r
2323 if( lightmapNum == 0 )
\r
2324 VectorCopy( ambientColor, luxel );
\r
2326 VectorClear( luxel );
\r
2327 luxel[ 3 ] = 1.0f;
\r
2330 /* store a bogus value to be fixed later */
\r
2333 VectorClear( luxel );
\r
2334 luxel[ 3 ] = -1.0f;
\r
2339 /* clean up and store into bsp luxels */
\r
2341 for( y = 0; y < lm->h; y++ )
\r
2343 for( x = 0; x < lm->w; x++ )
\r
2346 luxel = SUPER_LUXEL( lightmapNum, x, y );
\r
2347 deluxel = SUPER_DELUXEL( x, y );
\r
2349 /* copy light direction */
\r
2350 if( deluxemap && lightmapNum == 0 )
\r
2351 VectorCopy( deluxel, dirSample );
\r
2353 /* is this a valid sample? */
\r
2354 if( luxel[ 3 ] > 0.0f )
\r
2356 VectorCopy( luxel, sample );
\r
2357 samples = luxel[ 3 ];
\r
2361 /* fix negative samples */
\r
2362 for( j = 0; j < 3; j++ )
\r
2364 if( sample[ j ] < 0.0f )
\r
2365 sample[ j ] = 0.0f;
\r
2370 /* nick an average value from the neighbors */
\r
2371 VectorClear( sample );
\r
2372 VectorClear( dirSample );
\r
2375 /* fixme: why is this disabled?? */
\r
2376 for( sy = (y - 1); sy <= (y + 1); sy++ )
\r
2378 if( sy < 0 || sy >= lm->h )
\r
2381 for( sx = (x - 1); sx <= (x + 1); sx++ )
\r
2383 if( sx < 0 || sx >= lm->w || (sx == x && sy == y) )
\r
2386 /* get neighbor's particulars */
\r
2387 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
\r
2388 if( luxel[ 3 ] < 0.0f )
\r
2390 VectorAdd( sample, luxel, sample );
\r
2391 samples += luxel[ 3 ];
\r
2396 if( samples == 0.0f )
\r
2398 VectorSet( sample, -1.0f, -1.0f, -1.0f );
\r
2406 /* fix negative samples */
\r
2407 for( j = 0; j < 3; j++ )
\r
2409 if( sample[ j ] < 0.0f )
\r
2410 sample[ j ] = 0.0f;
\r
2415 /* scale the sample */
\r
2416 VectorScale( sample, (1.0f / samples), sample );
\r
2418 /* store the sample in the radiosity luxels */
\r
2421 radLuxel = RAD_LUXEL( lightmapNum, x, y );
\r
2422 VectorCopy( sample, radLuxel );
\r
2424 /* if only storing bounced light, early out here */
\r
2425 if( bounceOnly && !bouncing )
\r
2429 /* store the sample in the bsp luxels */
\r
2430 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
\r
2431 bspDeluxel = BSP_DELUXEL( x, y );
\r
2433 VectorAdd( bspLuxel, sample, bspLuxel );
\r
2434 if( deluxemap && lightmapNum == 0 )
\r
2435 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
\r
2439 /* wrap bsp luxels if necessary */
\r
2440 if( lm->wrap[ 0 ] )
\r
2442 for( y = 0; y < lm->h; y++ )
\r
2444 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
\r
2445 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
\r
2446 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
\r
2447 VectorScale( bspLuxel, 0.5f, bspLuxel );
\r
2448 VectorCopy( bspLuxel, bspLuxel2 );
\r
2449 if( deluxemap && lightmapNum == 0 )
\r
2451 bspDeluxel = BSP_DELUXEL( 0, y );
\r
2452 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
\r
2453 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
\r
2454 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
\r
2455 VectorCopy( bspDeluxel, bspDeluxel2 );
\r
2459 if( lm->wrap[ 1 ] )
\r
2461 for( x = 0; x < lm->w; x++ )
\r
2463 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
\r
2464 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
\r
2465 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
\r
2466 VectorScale( bspLuxel, 0.5f, bspLuxel );
\r
2467 VectorCopy( bspLuxel, bspLuxel2 );
\r
2468 if( deluxemap && lightmapNum == 0 )
\r
2470 bspDeluxel = BSP_DELUXEL( x, 0 );
\r
2471 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
\r
2472 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
\r
2473 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
\r
2474 VectorCopy( bspDeluxel, bspDeluxel2 );
\r
2481 /* -----------------------------------------------------------------
\r
2482 collapse non-unique lightmaps
\r
2483 ----------------------------------------------------------------- */
\r
2485 if( noCollapse == qfalse && deluxemap == qfalse )
\r
2488 Sys_FPrintf( SYS_VRB, "collapsing..." );
\r
2490 /* set all twin refs to null */
\r
2491 for( i = 0; i < numRawLightmaps; i++ )
\r
2493 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2495 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
\r
2496 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
\r
2497 rawLightmaps[ i ].numStyledTwins = 0;
\r
2501 /* walk the list of raw lightmaps */
\r
2502 for( i = 0; i < numRawLightmaps; i++ )
\r
2504 /* get lightmap */
\r
2505 lm = &rawLightmaps[ i ];
\r
2507 /* walk lightmaps */
\r
2508 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2511 if( lm->bspLuxels[ lightmapNum ] == NULL ||
\r
2512 lm->twins[ lightmapNum ] != NULL )
\r
2515 /* find all lightmaps that are virtually identical to this one */
\r
2516 for( j = i + 1; j < numRawLightmaps; j++ )
\r
2518 /* get lightmap */
\r
2519 lm2 = &rawLightmaps[ j ];
\r
2521 /* walk lightmaps */
\r
2522 for( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
\r
2525 if( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
\r
2526 lm2->twins[ lightmapNum2 ] != NULL )
\r
2529 /* compare them */
\r
2530 if( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
\r
2532 /* merge and set twin */
\r
2533 MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 );
\r
2534 lm2->twins[ lightmapNum2 ] = lm;
\r
2535 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
\r
2537 numTwinLuxels += (lm->w * lm->h);
\r
2539 /* count styled twins */
\r
2540 if( lightmapNum > 0 )
\r
2541 lm->numStyledTwins++;
\r
2549 /* -----------------------------------------------------------------
\r
2550 sort raw lightmaps by shader
\r
2551 ----------------------------------------------------------------- */
\r
2554 Sys_FPrintf( SYS_VRB, "sorting..." );
\r
2556 /* allocate a new sorted list */
\r
2557 if( sortLightmaps == NULL )
\r
2558 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
\r
2560 /* fill it out and sort it */
\r
2561 for( i = 0; i < numRawLightmaps; i++ )
\r
2562 sortLightmaps[ i ] = i;
\r
2563 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
\r
2565 /* -----------------------------------------------------------------
\r
2566 allocate output lightmaps
\r
2567 ----------------------------------------------------------------- */
\r
2570 Sys_FPrintf( SYS_VRB, "allocating..." );
\r
2572 /* kill all existing output lightmaps */
\r
2573 if( outLightmaps != NULL )
\r
2575 for( i = 0; i < numOutLightmaps; i++ )
\r
2577 free( outLightmaps[ i ].lightBits );
\r
2578 free( outLightmaps[ i ].bspLightBytes );
\r
2580 free( outLightmaps );
\r
2581 outLightmaps = NULL;
\r
2584 numLightmapShaders = 0;
\r
2585 numOutLightmaps = 0;
\r
2586 numBSPLightmaps = 0;
\r
2587 numExtLightmaps = 0;
\r
2589 /* find output lightmap */
\r
2590 for( i = 0; i < numRawLightmaps; i++ )
\r
2592 lm = &rawLightmaps[ sortLightmaps[ i ] ];
\r
2593 FindOutLightmaps( lm );
\r
2596 /* set output numbers in twinned lightmaps */
\r
2597 for( i = 0; i < numRawLightmaps; i++ )
\r
2599 /* get lightmap */
\r
2600 lm = &rawLightmaps[ sortLightmaps[ i ] ];
\r
2602 /* walk lightmaps */
\r
2603 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2606 lm2 = lm->twins[ lightmapNum ];
\r
2609 lightmapNum2 = lm->twinNums[ lightmapNum ];
\r
2611 /* find output lightmap from twin */
\r
2612 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
\r
2613 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
\r
2614 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
\r
2618 /* -----------------------------------------------------------------
\r
2619 store output lightmaps
\r
2620 ----------------------------------------------------------------- */
\r
2623 Sys_FPrintf( SYS_VRB, "storing..." );
\r
2625 /* count the bsp lightmaps and allocate space */
\r
2626 if( bspLightBytes != NULL )
\r
2627 free( bspLightBytes );
\r
2628 if( numBSPLightmaps == 0 || externalLightmaps )
\r
2630 numBSPLightBytes = 0;
\r
2631 bspLightBytes = NULL;
\r
2635 numBSPLightBytes = (numBSPLightmaps * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3);
\r
2636 bspLightBytes = safe_malloc( numBSPLightBytes );
\r
2637 memset( bspLightBytes, 0, numBSPLightBytes );
\r
2640 /* walk the list of output lightmaps */
\r
2641 for( i = 0; i < numOutLightmaps; i++ )
\r
2643 /* get output lightmap */
\r
2644 olm = &outLightmaps[ i ];
\r
2646 /* is this a valid bsp lightmap? */
\r
2647 if( olm->lightmapNum >= 0 && !externalLightmaps )
\r
2649 /* copy lighting data */
\r
2650 lb = bspLightBytes + (olm->lightmapNum * LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3);
\r
2651 memcpy( lb, olm->bspLightBytes, LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3 );
\r
2653 /* copy direction data */
\r
2656 lb = bspLightBytes + ((olm->lightmapNum + 1) * LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3);
\r
2657 memcpy( lb, olm->bspDirBytes, LIGHTMAP_HEIGHT * LIGHTMAP_WIDTH * 3 );
\r
2661 /* external lightmap? */
\r
2662 if( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps )
\r
2664 /* make a directory for the lightmaps */
\r
2665 Q_mkdir( dirname );
\r
2667 /* set external lightmap number */
\r
2668 olm->extLightmapNum = numExtLightmaps;
\r
2670 /* write lightmap */
\r
2671 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
\r
2672 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
\r
2673 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
\r
2674 numExtLightmaps++;
\r
2676 /* write deluxemap */
\r
2679 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
\r
2680 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
\r
2681 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
\r
2682 numExtLightmaps++;
\r
2684 if( debugDeluxemap )
\r
2685 olm->extLightmapNum++;
\r
2690 if( numExtLightmaps > 0 )
\r
2691 Sys_FPrintf( SYS_VRB, "\n" );
\r
2693 /* delete unused external lightmaps */
\r
2694 for( i = numExtLightmaps; i; i++ )
\r
2696 /* determine if file exists */
\r
2697 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
\r
2698 if( !FileExists( filename ) )
\r
2702 remove( filename );
\r
2705 /* -----------------------------------------------------------------
\r
2706 project the lightmaps onto the bsp surfaces
\r
2707 ----------------------------------------------------------------- */
\r
2710 Sys_FPrintf( SYS_VRB, "projecting..." );
\r
2712 /* walk the list of surfaces */
\r
2713 for( i = 0; i < numBSPDrawSurfaces; i++ )
\r
2715 /* get the surface and info */
\r
2716 ds = &bspDrawSurfaces[ i ];
\r
2717 info = &surfaceInfos[ i ];
\r
2721 /* handle surfaces with identical parent */
\r
2722 if( info->parentSurfaceNum >= 0 )
\r
2724 /* preserve original data and get parent */
\r
2725 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
\r
2726 memcpy( &dsTemp, ds, sizeof( *ds ) );
\r
2728 /* overwrite child with parent data */
\r
2729 memcpy( ds, parent, sizeof( *ds ) );
\r
2731 /* restore key parts */
\r
2732 ds->fogNum = dsTemp.fogNum;
\r
2733 ds->firstVert = dsTemp.firstVert;
\r
2734 ds->firstIndex = dsTemp.firstIndex;
\r
2735 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
\r
2737 /* set vertex data */
\r
2738 dv = &bspDrawVerts[ ds->firstVert ];
\r
2739 dvParent = &bspDrawVerts[ parent->firstVert ];
\r
2740 for( j = 0; j < ds->numVerts; j++ )
\r
2742 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
\r
2743 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
\r
2746 /* skip the rest */
\r
2750 /* handle vertex lit or approximated surfaces */
\r
2751 else if( lm == NULL || lm->outLightmapNums[ 0 ] < 0 )
\r
2753 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2755 ds->lightmapNum[ lightmapNum ] = -3;
\r
2756 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
\r
2760 /* handle lightmapped surfaces */
\r
2763 /* walk lightmaps */
\r
2764 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2767 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
\r
2769 /* handle unused style */
\r
2770 if( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
\r
2772 ds->lightmapNum[ lightmapNum ] = -3;
\r
2776 /* get output lightmap */
\r
2777 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
\r
2779 /* set bsp lightmap number */
\r
2780 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
\r
2782 /* deluxemap debugging makes the deluxemap visible */
\r
2783 if( deluxemap && debugDeluxemap && lightmapNum == 0 )
\r
2784 ds->lightmapNum[ lightmapNum ]++;
\r
2786 /* calc lightmap origin in texture space */
\r
2787 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
\r
2788 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
\r
2790 /* calc lightmap st coords and store lighting values */
\r
2791 dv = &bspDrawVerts[ ds->firstVert ];
\r
2792 ydv = &yDrawVerts[ ds->firstVert ];
\r
2793 for( j = 0; j < ds->numVerts; j++ )
\r
2795 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (ydv[ j ].lightmap[ 0 ][ 0 ] / (superSample * olm->customWidth));
\r
2796 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (ydv[ j ].lightmap[ 0 ][ 1 ] / (superSample * olm->customHeight));
\r
2801 /* store vertex colors */
\r
2802 dv = &bspDrawVerts[ ds->firstVert ];
\r
2803 for( j = 0; j < ds->numVerts; j++ )
\r
2805 /* walk lightmaps */
\r
2806 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2808 /* handle unused style */
\r
2809 if( ds->vertexStyles[ lightmapNum ] == LS_NONE )
\r
2810 VectorClear( color );
\r
2813 /* get vertex color */
\r
2814 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
\r
2815 VectorCopy( luxel, color );
\r
2817 /* set minimum light */
\r
2818 if( lightmapNum == 0 )
\r
2820 for( k = 0; k < 3; k++ )
\r
2821 if( color[ k ] < minVertexLight[ k ] )
\r
2822 color[ k ] = minVertexLight[ k ];
\r
2826 /* store to bytes */
\r
2827 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
\r
2831 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
\r
2832 if( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) //% info->si->styleMarker > 0 )
\r
2835 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
\r
2839 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
\r
2840 dv = &bspDrawVerts[ ds->firstVert ];
\r
2842 /* depthFunc equal? */
\r
2843 if( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED )
\r
2848 /* generate stages for styled lightmaps */
\r
2849 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
\r
2852 style = lm->styles[ lightmapNum ];
\r
2853 if( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
\r
2856 /* get output lightmap */
\r
2857 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
\r
2859 /* lightmap name */
\r
2860 if( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] )
\r
2861 strcpy( lightmapName, "$lightmap" );
\r
2863 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
\r
2865 /* get rgbgen string */
\r
2866 if( rgbGenValues[ style ] == NULL )
\r
2868 sprintf( key, "_style%drgbgen", style );
\r
2869 rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
\r
2870 if( rgbGenValues[ style ][ 0 ] == '\0' )
\r
2871 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
\r
2873 rgbGen[ 0 ] = '\0';
\r
2874 if( rgbGenValues[ style ][ 0 ] != '\0' )
\r
2875 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
\r
2877 rgbGen[ 0 ] = '\0';
\r
2879 /* get alphagen string */
\r
2880 if( alphaGenValues[ style ] == NULL )
\r
2882 sprintf( key, "_style%dalphagen", style );
\r
2883 alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
\r
2885 if( alphaGenValues[ style ][ 0 ] != '\0' )
\r
2886 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
\r
2888 alphaGen[ 0 ] = '\0';
\r
2890 /* calculate st offset */
\r
2891 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
\r
2892 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
\r
2894 /* create additional stage */
\r
2895 if( lmx == 0.0f && lmy == 0.0f )
\r
2897 sprintf( styleStage, "\t{\n"
\r
2898 "\t\tmap %s\n" /* lightmap */
\r
2899 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
\r
2900 "%s" /* depthFunc equal */
\r
2902 "%s" /* alphaGen */
\r
2903 "\t\ttcGen lightmap\n"
\r
2906 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
\r
2912 sprintf( styleStage, "\t{\n"
\r
2913 "\t\tmap %s\n" /* lightmap */
\r
2914 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
\r
2915 "%s" /* depthFunc equal */
\r
2917 "%s" /* alphaGen */
\r
2918 "\t\ttcGen lightmap\n"
\r
2919 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
\r
2922 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
\r
2930 strcat( styleStages, styleStage );
\r
2933 /* create custom shader */
\r
2934 if( info->si->styleMarker == 2 )
\r
2935 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
\r
2937 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
\r
2939 /* emit remap command */
\r
2940 //% EmitVertexRemapShader( csi->shader, info->si->shader );
\r
2943 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
\r
2944 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
\r
2945 //% Sys_Printf( ")\n" );
\r
2948 /* devise a custom shader for this surface (fixme: make this work with light styles) */
\r
2949 else if( olm != NULL && lm != NULL && !externalLightmaps &&
\r
2950 (olm->customWidth != LIGHTMAP_WIDTH || olm->customHeight != LIGHTMAP_HEIGHT) )
\r
2952 /* get output lightmap */
\r
2953 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
\r
2955 /* do some name mangling */
\r
2956 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
\r
2958 /* create custom shader */
\r
2959 csi = CustomShader( info->si, "$lightmap", lightmapName );
\r
2962 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
\r
2963 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
\r
2964 //% Sys_Printf( ")\n" );
\r
2967 /* use the normal plain-jane shader */
\r
2969 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
\r
2973 Sys_FPrintf( SYS_VRB, "done.\n" );
\r
2975 /* calc num stored */
\r
2976 numStored = numBSPLightBytes / 3;
\r
2977 efficiency = (numStored <= 0)
\r
2979 : (float) numUsed / (float) numStored;
\r
2982 Sys_Printf( "%9d luxels used\n", numUsed );
\r
2983 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
\r
2984 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
\r
2985 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
\r
2986 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
\r
2987 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
\r
2988 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
\r
2989 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
\r
2991 /* write map shader file */
\r
2992 WriteMapShaderFile();
\r