1 /* -------------------------------------------------------------------------------
2
3 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6 This file is part of GtkRadiant.
7
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.
12
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.
17
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
21
22 ----------------------------------------------------------------------------------
23
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."
26
27 ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define LIGHTMAPS_YDNAR_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41
42 /* -------------------------------------------------------------------------------
43
44 this file contains code that doe lightmap allocation and projection that
45 runs in the -light phase.
46
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.
50
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.
54
55 ------------------------------------------------------------------------------- */
56
57 /*
58 WriteTGA24()
59 based on WriteTGA() from imagelib.c
60 */
61
WriteTGA24(char * filename,byte * data,int width,int height,qboolean flip)62 void WriteTGA24( char *filename, byte *data, int width, int height, qboolean flip )
63 {
64 int i, c;
65 byte *buffer, *in;
66 FILE *file;
67
68
69 /* allocate a buffer and set it up */
70 buffer = safe_malloc( width * height * 3 + 18 );
71 memset( buffer, 0, 18 );
72 buffer[ 2 ] = 2;
73 buffer[ 12 ] = width & 255;
74 buffer[ 13 ] = width >> 8;
75 buffer[ 14 ] = height & 255;
76 buffer[ 15 ] = height >> 8;
77 buffer[ 16 ] = 24;
78
79 /* swap rgb to bgr */
80 c = (width * height * 3) + 18;
81 for( i = 18; i < c; i += 3 )
82 {
83 buffer[ i ] = data[ i - 18 + 2 ]; /* blue */
84 buffer[ i + 1 ] = data[ i - 18 + 1 ]; /* green */
85 buffer[ i + 2 ] = data[ i - 18 + 0 ]; /* red */
86 }
87
88 /* write it and free the buffer */
89 file = fopen( filename, "wb" );
90 if( file == NULL )
91 Error( "Unable to open %s for writing", filename );
92
93 /* flip vertically? */
94 if( flip )
95 {
96 fwrite( buffer, 1, 18, file );
97 for( in = buffer + ((height - 1) * width * 3) + 18; in >= buffer; in -= (width * 3) )
98 fwrite( in, 1, (width * 3), file );
99 }
100 else
101 fwrite( buffer, 1, c, file );
102
103 /* close the file */
104 fclose( file );
105 free( buffer );
106 }
107
108
109
110 /*
111 ExportLightmaps()
112 exports the lightmaps as a list of numbered tga images
113 */
114
ExportLightmaps(void)115 void ExportLightmaps( void )
116 {
117 int i;
118 char dirname[ 1024 ], filename[ 1024 ];
119 byte *lightmap;
120
121
122 /* note it */
123 Sys_FPrintf( SYS_VRB, "--- ExportLightmaps ---\n");
124
125 /* do some path mangling */
126 strcpy( dirname, source );
127 StripExtension( dirname );
128
129 /* sanity check */
130 if( bspLightBytes == NULL )
131 {
132 Sys_Printf( "WARNING: No BSP lightmap data\n" );
133 return;
134 }
135
136 /* make a directory for the lightmaps */
137 Q_mkdir( dirname );
138
139 /* iterate through the lightmaps */
140 for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (game->lightmapSize * game->lightmapSize * 3) )
141 {
142 /* write a tga image out */
143 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
144 Sys_Printf( "Writing %s\n", filename );
145 WriteTGA24( filename, lightmap, game->lightmapSize, game->lightmapSize, qfalse );
146 }
147 }
148
149
150
151 /*
152 ExportLightmapsMain()
153 exports the lightmaps as a list of numbered tga images
154 */
155
ExportLightmapsMain(int argc,char ** argv)156 int ExportLightmapsMain( int argc, char **argv )
157 {
158 /* arg checking */
159 if( argc < 1 )
160 {
161 Sys_Printf( "Usage: q3map -export [-v] <mapname>\n" );
162 return 0;
163 }
164
165 /* do some path mangling */
166 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
167 StripExtension( source );
168 DefaultExtension( source, ".bsp" );
169
170 /* load the bsp */
171 Sys_Printf( "Loading %s\n", source );
172 LoadBSPFile( source );
173
174 /* export the lightmaps */
175 ExportLightmaps();
176
177 /* return to sender */
178 return 0;
179 }
180
181
182
183 /*
184 ImportLightmapsMain()
185 imports the lightmaps from a list of numbered tga images
186 */
187
ImportLightmapsMain(int argc,char ** argv)188 int ImportLightmapsMain( int argc, char **argv )
189 {
190 int i, x, y, len, width, height;
191 char dirname[ 1024 ], filename[ 1024 ];
192 byte *lightmap, *buffer, *pixels, *in, *out;
193
194
195 /* arg checking */
196 if( argc < 1 )
197 {
198 Sys_Printf( "Usage: q3map -import [-v] <mapname>\n" );
199 return 0;
200 }
201
202 /* do some path mangling */
203 strcpy( source, ExpandArg( argv[ argc - 1 ] ) );
204 StripExtension( source );
205 DefaultExtension( source, ".bsp" );
206
207 /* load the bsp */
208 Sys_Printf( "Loading %s\n", source );
209 LoadBSPFile( source );
210
211 /* note it */
212 Sys_FPrintf( SYS_VRB, "--- ImportLightmaps ---\n");
213
214 /* do some path mangling */
215 strcpy( dirname, source );
216 StripExtension( dirname );
217
218 /* sanity check */
219 if( bspLightBytes == NULL )
220 Error( "No lightmap data" );
221
222 /* make a directory for the lightmaps */
223 Q_mkdir( dirname );
224
225 /* iterate through the lightmaps */
226 for( i = 0, lightmap = bspLightBytes; lightmap < (bspLightBytes + numBSPLightBytes); i++, lightmap += (game->lightmapSize * game->lightmapSize * 3) )
227 {
228 /* read a tga image */
229 sprintf( filename, "%s/lightmap_%04d.tga", dirname, i );
230 Sys_Printf( "Loading %s\n", filename );
231 buffer = NULL;
232 len = vfsLoadFile( filename, (void*) &buffer, -1 );
233 if( len < 0 )
234 {
235 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
236 continue;
237 }
238
239 /* parse file into an image */
240 pixels = NULL;
241 LoadTGABuffer( buffer, buffer + len, &pixels, &width, &height );
242 free( buffer );
243
244 /* sanity check it */
245 if( pixels == NULL )
246 {
247 Sys_Printf( "WARNING: Unable to load image %s\n", filename );
248 continue;
249 }
250 if( width != game->lightmapSize || height != game->lightmapSize )
251 Sys_Printf( "WARNING: Image %s is not the right size (%d, %d) != (%d, %d)\n",
252 filename, width, height, game->lightmapSize, game->lightmapSize );
253
254 /* copy the pixels */
255 in = pixels;
256 for( y = 1; y <= game->lightmapSize; y++ )
257 {
258 out = lightmap + ((game->lightmapSize - y) * game->lightmapSize * 3);
259 for( x = 0; x < game->lightmapSize; x++, in += 4, out += 3 )
260 VectorCopy( in, out );
261 }
262
263 /* free the image */
264 free( pixels );
265 }
266
267 /* write the bsp */
268 Sys_Printf( "writing %s\n", source );
269 WriteBSPFile( source );
270
271 /* return to sender */
272 return 0;
273 }
274
275
276
277 /* -------------------------------------------------------------------------------
278
279 this section deals with projecting a lightmap onto a raw drawsurface
280
281 ------------------------------------------------------------------------------- */
282
283 /*
284 CompareLightSurface()
285 compare function for qsort()
286 */
287
CompareLightSurface(const void * a,const void * b)288 static int CompareLightSurface( const void *a, const void *b )
289 {
290 shaderInfo_t *asi, *bsi;
291
292
293 /* get shaders */
294 asi = surfaceInfos[ *((int*) a) ].si;
295 bsi = surfaceInfos[ *((int*) b) ].si;
296
297 /* dummy check */
298 if( asi == NULL )
299 return -1;
300 if( bsi == NULL )
301 return 1;
302
303 /* compare shader names */
304 return strcmp( asi->shader, bsi->shader );
305 }
306
307
308
309 /*
310 FinishRawLightmap()
311 allocates a raw lightmap's necessary buffers
312 */
313
FinishRawLightmap(rawLightmap_t * lm)314 void FinishRawLightmap( rawLightmap_t *lm )
315 {
316 int i, j, c, size, *sc;
317 float is;
318 surfaceInfo_t *info;
319
320
321 /* sort light surfaces by shader name */
322 qsort( &lightSurfaces[ lm->firstLightSurface ], lm->numLightSurfaces, sizeof( int ), CompareLightSurface );
323
324 /* count clusters */
325 lm->numLightClusters = 0;
326 for( i = 0; i < lm->numLightSurfaces; i++ )
327 {
328 /* get surface info */
329 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
330
331 /* add surface clusters */
332 lm->numLightClusters += info->numSurfaceClusters;
333 }
334
335 /* allocate buffer for clusters and copy */
336 lm->lightClusters = safe_malloc( lm->numLightClusters * sizeof( *lm->lightClusters ) );
337 c = 0;
338 for( i = 0; i < lm->numLightSurfaces; i++ )
339 {
340 /* get surface info */
341 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
342
343 /* add surface clusters */
344 for( j = 0; j < info->numSurfaceClusters; j++ )
345 lm->lightClusters[ c++ ] = surfaceClusters[ info->firstSurfaceCluster + j ];
346 }
347
348 /* set styles */
349 lm->styles[ 0 ] = LS_NORMAL;
350 for( i = 1; i < MAX_LIGHTMAPS; i++ )
351 lm->styles[ i ] = LS_NONE;
352
353 /* set supersampling size */
354 lm->sw = lm->w * superSample;
355 lm->sh = lm->h * superSample;
356
357 /* add to super luxel count */
358 numRawSuperLuxels += (lm->sw * lm->sh);
359
360 /* manipulate origin/vecs for supersampling */
361 if( superSample > 1 && lm->vecs != NULL )
362 {
363 /* calc inverse supersample */
364 is = 1.0f / superSample;
365
366 /* scale the vectors and shift the origin */
367 #if 1
368 /* new code that works for arbitrary supersampling values */
369 VectorMA( lm->origin, -0.5, lm->vecs[ 0 ], lm->origin );
370 VectorMA( lm->origin, -0.5, lm->vecs[ 1 ], lm->origin );
371 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
372 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
373 VectorMA( lm->origin, is, lm->vecs[ 0 ], lm->origin );
374 VectorMA( lm->origin, is, lm->vecs[ 1 ], lm->origin );
375 #else
376 /* old code that only worked with a value of 2 */
377 VectorScale( lm->vecs[ 0 ], is, lm->vecs[ 0 ] );
378 VectorScale( lm->vecs[ 1 ], is, lm->vecs[ 1 ] );
379 VectorMA( lm->origin, -is, lm->vecs[ 0 ], lm->origin );
380 VectorMA( lm->origin, -is, lm->vecs[ 1 ], lm->origin );
381 #endif
382 }
383
384 /* allocate bsp lightmap storage */
385 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
386 if( lm->bspLuxels[ 0 ] == NULL )
387 lm->bspLuxels[ 0 ] = safe_malloc( size );
388 memset( lm->bspLuxels[ 0 ], 0, size );
389
390 /* allocate radiosity lightmap storage */
391 if( bounce )
392 {
393 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
394 if( lm->radLuxels[ 0 ] == NULL )
395 lm->radLuxels[ 0 ] = safe_malloc( size );
396 memset( lm->radLuxels[ 0 ], 0, size );
397 }
398
399 /* allocate sampling lightmap storage */
400 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
401 if( lm->superLuxels[ 0 ] == NULL )
402 lm->superLuxels[ 0 ] = safe_malloc( size );
403 memset( lm->superLuxels[ 0 ], 0, size );
404
405 /* allocate origin map storage */
406 size = lm->sw * lm->sh * SUPER_ORIGIN_SIZE * sizeof( float );
407 if( lm->superOrigins == NULL )
408 lm->superOrigins = safe_malloc( size );
409 memset( lm->superOrigins, 0, size );
410
411 /* allocate normal map storage */
412 size = lm->sw * lm->sh * SUPER_NORMAL_SIZE * sizeof( float );
413 if( lm->superNormals == NULL )
414 lm->superNormals = safe_malloc( size );
415 memset( lm->superNormals, 0, size );
416
417 /* allocate cluster map storage */
418 size = lm->sw * lm->sh * sizeof( int );
419 if( lm->superClusters == NULL )
420 lm->superClusters = safe_malloc( size );
421 size = lm->sw * lm->sh;
422 sc = lm->superClusters;
423 for( i = 0; i < size; i++ )
424 (*sc++) = CLUSTER_UNMAPPED;
425
426 /* deluxemap allocation */
427 if( deluxemap )
428 {
429 /* allocate sampling deluxel storage */
430 size = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
431 if( lm->superDeluxels == NULL )
432 lm->superDeluxels = safe_malloc( size );
433 memset( lm->superDeluxels, 0, size );
434
435 /* allocate bsp deluxel storage */
436 size = lm->w * lm->h * BSP_DELUXEL_SIZE * sizeof( float );
437 if( lm->bspDeluxels == NULL )
438 lm->bspDeluxels = safe_malloc( size );
439 memset( lm->bspDeluxels, 0, size );
440 }
441
442 /* add to count */
443 numLuxels += (lm->sw * lm->sh);
444 }
445
446
447
448 /*
449 AddPatchToRawLightmap()
450 projects a lightmap for a patch surface
451 since lightmap calculation for surfaces is now handled in a general way (light_ydnar.c),
452 it is no longer necessary for patch verts to fall exactly on a lightmap sample
453 based on AllocateLightmapForPatch()
454 */
455
AddPatchToRawLightmap(int num,rawLightmap_t * lm)456 qboolean AddPatchToRawLightmap( int num, rawLightmap_t *lm )
457 {
458 bspDrawSurface_t *ds;
459 surfaceInfo_t *info;
460 int x, y;
461 bspDrawVert_t *verts, *a, *b;
462 vec3_t delta;
463 mesh_t src, *subdivided, *mesh;
464 float sBasis, tBasis, s, t;
465 float length, widthTable[ MAX_EXPANDED_AXIS ], heightTable[ MAX_EXPANDED_AXIS ];
466
467
468 /* patches finish a raw lightmap */
469 lm->finished = qtrue;
470
471 /* get surface and info */
472 ds = &bspDrawSurfaces[ num ];
473 info = &surfaceInfos[ num ];
474
475 /* make a temporary mesh from the drawsurf */
476 src.width = ds->patchWidth;
477 src.height = ds->patchHeight;
478 src.verts = &yDrawVerts[ ds->firstVert ];
479 //% subdivided = SubdivideMesh( src, 8, 512 );
480 subdivided = SubdivideMesh2( src, info->patchIterations );
481
482 /* fit it to the curve and remove colinear verts on rows/columns */
483 PutMeshOnCurve( *subdivided );
484 mesh = RemoveLinearMeshColumnsRows( subdivided );
485 FreeMesh( subdivided );
486
487 /* find the longest distance on each row/column */
488 verts = mesh->verts;
489 memset( widthTable, 0, sizeof( widthTable ) );
490 memset( heightTable, 0, sizeof( heightTable ) );
491 for( y = 0; y < mesh->height; y++ )
492 {
493 for( x = 0; x < mesh->width; x++ )
494 {
495 /* get width */
496 if( x + 1 < mesh->width )
497 {
498 a = &verts[ (y * mesh->width) + x ];
499 b = &verts[ (y * mesh->width) + x + 1 ];
500 VectorSubtract( a->xyz, b->xyz, delta );
501 length = VectorLength( delta );
502 if( length > widthTable[ x ] )
503 widthTable[ x ] = length;
504 }
505
506 /* get height */
507 if( y + 1 < mesh->height )
508 {
509 a = &verts[ (y * mesh->width) + x ];
510 b = &verts[ ((y + 1) * mesh->width) + x ];
511 VectorSubtract( a->xyz, b->xyz, delta );
512 length = VectorLength( delta );
513 if( length > heightTable[ y ] )
514 heightTable[ y ] = length;
515 }
516 }
517 }
518
519 /* determine lightmap width */
520 length = 0;
521 for( x = 0; x < (mesh->width - 1); x++ )
522 length += widthTable[ x ];
523 lm->w = ceil( length / lm->sampleSize ) + 1;
524 if( lm->w < ds->patchWidth )
525 lm->w = ds->patchWidth;
526 if( lm->w > lm->customWidth )
527 lm->w = lm->customWidth;
528 sBasis = (float) (lm->w - 1) / (float) (ds->patchWidth - 1);
529
530 /* determine lightmap height */
531 length = 0;
532 for( y = 0; y < (mesh->height - 1); y++ )
533 length += heightTable[ y ];
534 lm->h = ceil( length / lm->sampleSize ) + 1;
535 if( lm->h < ds->patchHeight )
536 lm->h = ds->patchHeight;
537 if( lm->h > lm->customHeight )
538 lm->h = lm->customHeight;
539 tBasis = (float) (lm->h - 1) / (float) (ds->patchHeight - 1);
540
541 /* free the temporary mesh */
542 FreeMesh( mesh );
543
544 /* set the lightmap texture coordinates in yDrawVerts */
545 lm->wrap[ 0 ] = qtrue;
546 lm->wrap[ 1 ] = qtrue;
547 verts = &yDrawVerts[ ds->firstVert ];
548 for( y = 0; y < ds->patchHeight; y++ )
549 {
550 t = (tBasis * y) + 0.5f;
551 for( x = 0; x < ds->patchWidth; x++ )
552 {
553 s = (sBasis * x) + 0.5f;
554 verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 0 ] = s * superSample;
555 verts[ (y * ds->patchWidth) + x ].lightmap[ 0 ][ 1 ] = t * superSample;
556
557 if( y == 0 && !VectorCompare( verts[ x ].xyz, verts[ ((ds->patchHeight - 1) * ds->patchWidth) + x ].xyz ) )
558 lm->wrap[ 1 ] = qfalse;
559 }
560
561 if( !VectorCompare( verts[ (y * ds->patchWidth) ].xyz, verts[ (y * ds->patchWidth) + (ds->patchWidth - 1) ].xyz ) )
562 lm->wrap[ 0 ] = qfalse;
563 }
564
565 /* debug code: */
566 //% Sys_Printf( "wrap S: %d wrap T: %d\n", lm->wrap[ 0 ], lm->wrap[ 1 ] );
567 //% if( lm->w > (ds->lightmapWidth & 0xFF) || lm->h > (ds->lightmapHeight & 0xFF) )
568 //% Sys_Printf( "Patch lightmap: (%3d %3d) > (%3d, %3d)\n", lm->w, lm->h, ds->lightmapWidth & 0xFF, ds->lightmapHeight & 0xFF );
569 //% ds->lightmapWidth = lm->w | (ds->lightmapWidth & 0xFFFF0000);
570 //% ds->lightmapHeight = lm->h | (ds->lightmapHeight & 0xFFFF0000);
571
572 /* add to counts */
573 numPatchesLightmapped++;
574
575 /* return */
576 return qtrue;
577 }
578
579
580
581 /*
582 AddSurfaceToRawLightmap()
583 projects a lightmap for a surface
584 based on AllocateLightmapForSurface()
585 */
586
AddSurfaceToRawLightmap(int num,rawLightmap_t * lm)587 qboolean AddSurfaceToRawLightmap( int num, rawLightmap_t *lm )
588 {
589 bspDrawSurface_t *ds, *ds2;
590 surfaceInfo_t *info, *info2;
591 int num2, n, i, axisNum;
592 float s, t, d, len, sampleSize;
593 vec3_t mins, maxs, origin, faxis, size, exactSize, delta, normalized, vecs[ 2 ];
594 vec4_t plane;
595 bspDrawVert_t *verts;
596
597
598 /* get surface and info */
599 ds = &bspDrawSurfaces[ num ];
600 info = &surfaceInfos[ num ];
601
602 /* add the surface to the raw lightmap */
603 lightSurfaces[ numLightSurfaces++ ] = num;
604 lm->numLightSurfaces++;
605
606 /* does this raw lightmap already have any surfaces? */
607 if( lm->numLightSurfaces > 1 )
608 {
609 /* surface and raw lightmap must have the same lightmap projection axis */
610 if( VectorCompare( info->axis, lm->axis ) == qfalse )
611 return qfalse;
612
613 /* match identical attributes */
614 if( info->sampleSize != lm->sampleSize ||
615 info->entityNum != lm->entityNum ||
616 info->recvShadows != lm->recvShadows ||
617 info->si->lmCustomWidth != lm->customWidth ||
618 info->si->lmCustomHeight != lm->customHeight ||
619 info->si->lmBrightness != lm->brightness ||
620 info->si->lmFilterRadius != lm->filterRadius ||
621 info->si->splotchFix != lm->splotchFix )
622 return qfalse;
623
624 /* surface bounds must intersect with raw lightmap bounds */
625 for( i = 0; i < 3; i++ )
626 {
627 if( info->mins[ i ] > lm->maxs[ i ] )
628 return qfalse;
629 if( info->maxs[ i ] < lm->mins[ i ] )
630 return qfalse;
631 }
632
633 /* plane check (fixme: allow merging of nonplanars) */
634 if( info->si->lmMergable == qfalse )
635 {
636 if( info->plane == NULL || lm->plane == NULL )
637 return qfalse;
638
639 /* compare planes */
640 for( i = 0; i < 4; i++ )
641 if( fabs( info->plane[ i ] - lm->plane[ i ] ) > EQUAL_EPSILON )
642 return qfalse;
643 }
644
645 /* debug code hacking */
646 //% if( lm->numLightSurfaces > 1 )
647 //% return qfalse;
648 }
649
650 /* set plane */
651 if( info->plane == NULL )
652 lm->plane = NULL;
653
654 /* add surface to lightmap bounds */
655 AddPointToBounds( info->mins, lm->mins, lm->maxs );
656 AddPointToBounds( info->maxs, lm->mins, lm->maxs );
657
658 /* check to see if this is a non-planar patch */
659 if( ds->surfaceType == MST_PATCH &&
660 lm->axis[ 0 ] == 0.0f && lm->axis[ 1 ] == 0.0f && lm->axis[ 2 ] == 0.0f )
661 return AddPatchToRawLightmap( num, lm );
662
663 /* start with initially requested sample size */
664 sampleSize = lm->sampleSize;
665
666 /* round to the lightmap resolution */
667 for( i = 0; i < 3; i++ )
668 {
669 exactSize[ i ] = lm->maxs[ i ] - lm->mins[ i ];
670 mins[ i ] = sampleSize * floor( lm->mins[ i ] / sampleSize );
671 maxs[ i ] = sampleSize * ceil( lm->maxs[ i ] / sampleSize );
672 size[ i ] = (maxs[ i ] - mins[ i ]) / sampleSize + 1.0f;
673
674 /* hack (god this sucks) */
675 if( size[ i ] > lm->customWidth || size[ i ] > lm->customHeight )
676 {
677 i = -1;
678 sampleSize += 1.0f;
679 }
680 }
681
682 /* set actual sample size */
683 lm->actualSampleSize = sampleSize;
684
685 /* fixme: copy rounded mins/maxes to lightmap record? */
686 if( lm->plane == NULL )
687 {
688 VectorCopy( mins, lm->mins );
689 VectorCopy( maxs, lm->maxs );
690 VectorCopy( mins, origin );
691 }
692
693 /* set lightmap origin */
694 VectorCopy( lm->mins, origin );
695
696 /* make absolute axis */
697 faxis[ 0 ] = fabs( lm->axis[ 0 ] );
698 faxis[ 1 ] = fabs( lm->axis[ 1 ] );
699 faxis[ 2 ] = fabs( lm->axis[ 2 ] );
700
701 /* clear out lightmap vectors */
702 memset( vecs, 0, sizeof( vecs ) );
703
704 /* classify the plane (x y or z major) (ydnar: biased to z axis projection) */
705 if( faxis[ 2 ] >= faxis[ 0 ] && faxis[ 2 ] >= faxis[ 1 ] )
706 {
707 axisNum = 2;
708 lm->w = size[ 0 ];
709 lm->h = size[ 1 ];
710 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
711 vecs[ 1 ][ 1 ] = 1.0f / sampleSize;
712 }
713 else if( faxis[ 0 ] >= faxis[ 1 ] && faxis[ 0 ] >= faxis[ 2 ] )
714 {
715 axisNum = 0;
716 lm->w = size[ 1 ];
717 lm->h = size[ 2 ];
718 vecs[ 0 ][ 1 ] = 1.0f / sampleSize;
719 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
720 }
721 else
722 {
723 axisNum = 1;
724 lm->w = size[ 0 ];
725 lm->h = size[ 2 ];
726 vecs[ 0 ][ 0 ] = 1.0f / sampleSize;
727 vecs[ 1 ][ 2 ] = 1.0f / sampleSize;
728 }
729
730 /* check for bogus axis */
731 if( faxis[ axisNum ] == 0.0f )
732 {
733 Sys_Printf( "WARNING: ProjectSurfaceLightmap: Chose a 0 valued axis\n" );
734 lm->w = lm->h = 0;
735 return qfalse;
736 }
737
738 /* store the axis number in the lightmap */
739 lm->axisNum = axisNum;
740
741 /* walk the list of surfaces on this raw lightmap */
742 for( n = 0; n < lm->numLightSurfaces; n++ )
743 {
744 /* get surface */
745 num2 = lightSurfaces[ lm->firstLightSurface + n ];
746 ds2 = &bspDrawSurfaces[ num2 ];
747 info2 = &surfaceInfos[ num2 ];
748 verts = &yDrawVerts[ ds2->firstVert ];
749
750 /* set the lightmap texture coordinates in yDrawVerts in [0, superSample * lm->customWidth] space */
751 for( i = 0; i < ds2->numVerts; i++ )
752 {
753 VectorSubtract( verts[ i ].xyz, origin, delta );
754 s = DotProduct( delta, vecs[ 0 ] ) + 0.5f;
755 t = DotProduct( delta, vecs[ 1 ] ) + 0.5f;
756 verts[ i ].lightmap[ 0 ][ 0 ] = s * superSample;
757 verts[ i ].lightmap[ 0 ][ 1 ] = t * superSample;
758
759 if( s > (float) lm->w || t > (float) lm->h )
760 {
761 Sys_FPrintf( SYS_VRB, "WARNING: Lightmap texture coords out of range: S %1.4f > %3d || T %1.4f > %3d\n",
762 s, lm->w, t, lm->h );
763 }
764 }
765 }
766
767 /* get first drawsurface */
768 num2 = lightSurfaces[ lm->firstLightSurface ];
769 ds2 = &bspDrawSurfaces[ num2 ];
770 info2 = &surfaceInfos[ num2 ];
771 verts = &yDrawVerts[ ds2->firstVert ];
772
773 /* calculate lightmap origin */
774 if( VectorLength( ds2->lightmapVecs[ 2 ] ) )
775 VectorCopy( ds2->lightmapVecs[ 2 ], plane );
776 else
777 VectorCopy( lm->axis, plane );
778 plane[ 3 ] = DotProduct( verts[ 0 ].xyz, plane );
779
780 VectorCopy( origin, lm->origin );
781 d = DotProduct( lm->origin, plane ) - plane[ 3 ];
782 d /= plane[ axisNum ];
783 lm->origin[ axisNum ] -= d;
784
785 /* legacy support */
786 VectorCopy( lm->origin, ds->lightmapOrigin );
787
788 /* for planar surfaces, create lightmap vectors for st->xyz conversion */
789 if( VectorLength( ds->lightmapVecs[ 2 ] ) || 1 ) /* ydnar: can't remember what exactly i was thinking here... */
790 {
791 /* allocate space for the vectors */
792 lm->vecs = safe_malloc( 3 * sizeof( vec3_t ) );
793 memset( lm->vecs, 0, 3 * sizeof( vec3_t ) );
794 VectorCopy( ds->lightmapVecs[ 2 ], lm->vecs[ 2 ] );
795
796 /* project stepped lightmap blocks and subtract to get planevecs */
797 for( i = 0; i < 2; i++ )
798 {
799 len = VectorNormalize( vecs[ i ], normalized );
800 VectorScale( normalized, (1.0 / len), lm->vecs[ i ] );
801 d = DotProduct( lm->vecs[ i ], plane );
802 d /= plane[ axisNum ];
803 lm->vecs[ i ][ axisNum ] -= d;
804 }
805 }
806 else
807 {
808 /* lightmap vectors are useless on a non-planar surface */
809 lm->vecs = NULL;
810 }
811
812 /* add to counts */
813 if( ds->surfaceType == MST_PATCH )
814 {
815 numPatchesLightmapped++;
816 if( lm->plane != NULL )
817 numPlanarPatchesLightmapped++;
818 }
819 else
820 {
821 if( lm->plane != NULL )
822 numPlanarsLightmapped++;
823 else
824 numNonPlanarsLightmapped++;
825 }
826
827 /* return */
828 return qtrue;
829 }
830
831
832
833 /*
834 CompareSurfaceInfo()
835 compare function for qsort()
836 */
837
CompareSurfaceInfo(const void * a,const void * b)838 static int CompareSurfaceInfo( const void *a, const void *b )
839 {
840 surfaceInfo_t *aInfo, *bInfo;
841 int i;
842
843
844 /* get surface info */
845 aInfo = &surfaceInfos[ *((int*) a) ];
846 bInfo = &surfaceInfos[ *((int*) b) ];
847
848 /* model first */
849 if( aInfo->model < bInfo->model )
850 return 1;
851 else if( aInfo->model > bInfo->model )
852 return -1;
853
854 /* then lightmap status */
855 if( aInfo->hasLightmap < bInfo->hasLightmap )
856 return 1;
857 else if( aInfo->hasLightmap > bInfo->hasLightmap )
858 return -1;
859
860 /* then lightmap sample size */
861 if( aInfo->sampleSize < bInfo->sampleSize )
862 return 1;
863 else if( aInfo->sampleSize > bInfo->sampleSize )
864 return -1;
865
866 /* then lightmap axis */
867 for( i = 0; i < 3; i++ )
868 {
869 if( aInfo->axis[ i ] < bInfo->axis[ i ] )
870 return 1;
871 else if( aInfo->axis[ i ] > bInfo->axis[ i ] )
872 return -1;
873 }
874
875 /* then plane */
876 if( aInfo->plane == NULL && bInfo->plane != NULL )
877 return 1;
878 else if( aInfo->plane != NULL && bInfo->plane == NULL )
879 return -1;
880 else if( aInfo->plane != NULL && bInfo->plane != NULL )
881 {
882 for( i = 0; i < 4; i++ )
883 {
884 if( aInfo->plane[ i ] < bInfo->plane[ i ] )
885 return 1;
886 else if( aInfo->plane[ i ] > bInfo->plane[ i ] )
887 return -1;
888 }
889 }
890
891 /* then position in world */
892 for( i = 0; i < 3; i++ )
893 {
894 if( aInfo->mins[ i ] < bInfo->mins[ i ] )
895 return 1;
896 else if( aInfo->mins[ i ] > bInfo->mins[ i ] )
897 return -1;
898 }
899
900 /* these are functionally identical (this should almost never happen) */
901 return 0;
902 }
903
904
905
906 /*
907 SetupSurfaceLightmaps()
908 allocates lightmaps for every surface in the bsp that needs one
909 this depends on yDrawVerts being allocated
910 */
911
SetupSurfaceLightmaps(void)912 void SetupSurfaceLightmaps( void )
913 {
914 int i, j, k, s,num, num2;
915 bspModel_t *model;
916 bspLeaf_t *leaf;
917 bspDrawSurface_t *ds, *ds2;
918 surfaceInfo_t *info, *info2;
919 rawLightmap_t *lm;
920 qboolean added;
921 vec3_t mapSize, entityOrigin;
922
923
924 /* note it */
925 Sys_FPrintf( SYS_VRB, "--- SetupSurfaceLightmaps ---\n");
926
927 /* determine supersample amount */
928 if( superSample < 1 )
929 superSample = 1;
930 else if( superSample > 8 )
931 {
932 Sys_Printf( "WARNING: Insane supersampling amount (%d) detected.\n", superSample );
933 superSample = 8;
934 }
935
936 /* clear map bounds */
937 ClearBounds( mapMins, mapMaxs );
938
939 /* allocate a list of surface clusters */
940 numSurfaceClusters = 0;
941 maxSurfaceClusters = numBSPLeafSurfaces;
942 surfaceClusters = safe_malloc( maxSurfaceClusters * sizeof( *surfaceClusters ) );
943 memset( surfaceClusters, 0, maxSurfaceClusters * sizeof( *surfaceClusters ) );
944
945 /* allocate a list for per-surface info */
946 surfaceInfos = safe_malloc( numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
947 memset( surfaceInfos, 0, numBSPDrawSurfaces * sizeof( *surfaceInfos ) );
948 for( i = 0; i < numBSPDrawSurfaces; i++ )
949 surfaceInfos[ i ].childSurfaceNum = -1;
950
951 /* allocate a list of surface indexes to be sorted */
952 sortSurfaces = safe_malloc( numBSPDrawSurfaces * sizeof( int ) );
953 memset( sortSurfaces, 0, numBSPDrawSurfaces * sizeof( int ) );
954
955 /* walk each model in the bsp */
956 for( i = 0; i < numBSPModels; i++ )
957 {
958 /* get model */
959 model = &bspModels[ i ];
960
961 /* walk the list of surfaces in this model and fill out the info structs */
962 for( j = 0; j < model->numBSPSurfaces; j++ )
963 {
964 /* make surface index */
965 num = model->firstBSPSurface + j;
966
967 /* copy index to sort list */
968 sortSurfaces[ num ] = num;
969
970 /* get surface and info */
971 ds = &bspDrawSurfaces[ num ];
972 info = &surfaceInfos[ num ];
973
974 /* set entity origin */
975 if( ds->numVerts > 0 )
976 VectorSubtract( yDrawVerts[ ds->firstVert ].xyz, bspDrawVerts[ ds->firstVert ].xyz, entityOrigin );
977 else
978 VectorClear( entityOrigin );
979
980 /* basic setup */
981 info->model = model;
982 info->lm = NULL;
983 info->plane = NULL;
984 info->firstSurfaceCluster = numSurfaceClusters;
985
986 /* get extra data */
987 info->si = GetSurfaceExtraShaderInfo( num );
988 if( info->si == NULL )
989 info->si = ShaderInfoForShader( bspShaders[ ds->shaderNum ].shader );
990 info->parentSurfaceNum = GetSurfaceExtraParentSurfaceNum( num );
991 info->entityNum = GetSurfaceExtraEntityNum( num );
992 info->castShadows = GetSurfaceExtraCastShadows( num );
993 info->recvShadows = GetSurfaceExtraRecvShadows( num );
994 info->sampleSize = GetSurfaceExtraSampleSize( num );
995 info->longestCurve = GetSurfaceExtraLongestCurve( num );
996 info->patchIterations = IterationsForCurve( info->longestCurve, patchSubdivisions );
997 GetSurfaceExtraLightmapAxis( num, info->axis );
998
999 /* mark parent */
1000 if( info->parentSurfaceNum >= 0 )
1001 surfaceInfos[ info->parentSurfaceNum ].childSurfaceNum = j;
1002
1003 /* determine surface bounds */
1004 ClearBounds( info->mins, info->maxs );
1005 for( k = 0; k < ds->numVerts; k++ )
1006 {
1007 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, mapMins, mapMaxs );
1008 AddPointToBounds( yDrawVerts[ ds->firstVert + k ].xyz, info->mins, info->maxs );
1009 }
1010
1011 /* find all the bsp clusters the surface falls into */
1012 for( k = 0; k < numBSPLeafs; k++ )
1013 {
1014 /* get leaf */
1015 leaf = &bspLeafs[ k ];
1016
1017 /* test bbox */
1018 if( leaf->mins[ 0 ] > info->maxs[ 0 ] || leaf->maxs[ 0 ] < info->mins[ 0 ] ||
1019 leaf->mins[ 1 ] > info->maxs[ 1 ] || leaf->maxs[ 1 ] < info->mins[ 1 ] ||
1020 leaf->mins[ 2 ] > info->maxs[ 2 ] || leaf->maxs[ 2 ] < info->mins[ 2 ] )
1021 continue;
1022
1023 /* test leaf surfaces */
1024 for( s = 0; s < leaf->numBSPLeafSurfaces; s++ )
1025 {
1026 if( bspLeafSurfaces[ leaf->firstBSPLeafSurface + s ] == num )
1027 {
1028 if( numSurfaceClusters >= maxSurfaceClusters )
1029 Error( "maxSurfaceClusters exceeded" );
1030 surfaceClusters[ numSurfaceClusters ] = leaf->cluster;
1031 numSurfaceClusters++;
1032 info->numSurfaceClusters++;
1033 }
1034 }
1035 }
1036
1037 /* determine if surface is planar */
1038 if( VectorLength( ds->lightmapVecs[ 2 ] ) > 0.0f )
1039 {
1040 /* make a plane */
1041 info->plane = safe_malloc( 4 * sizeof( float ) );
1042 VectorCopy( ds->lightmapVecs[ 2 ], info->plane );
1043 info->plane[ 3 ] = DotProduct( yDrawVerts[ ds->firstVert ].xyz, info->plane );
1044 }
1045
1046 /* determine if surface requires a lightmap */
1047 if( ds->surfaceType == MST_TRIANGLE_SOUP ||
1048 ds->surfaceType == MST_FOLIAGE ||
1049 (info->si->compileFlags & C_VERTEXLIT) )
1050 numSurfsVertexLit++;
1051 else
1052 {
1053 numSurfsLightmapped++;
1054 info->hasLightmap = qtrue;
1055 }
1056 }
1057 }
1058
1059 /* find longest map distance */
1060 VectorSubtract( mapMaxs, mapMins, mapSize );
1061 maxMapDistance = VectorLength( mapSize );
1062
1063 /* sort the surfaces info list */
1064 qsort( sortSurfaces, numBSPDrawSurfaces, sizeof( int ), CompareSurfaceInfo );
1065
1066 /* allocate a list of surfaces that would go into raw lightmaps */
1067 numLightSurfaces = 0;
1068 lightSurfaces = safe_malloc( numSurfsLightmapped * sizeof( int ) );
1069 memset( lightSurfaces, 0, numSurfsLightmapped * sizeof( int ) );
1070
1071 /* allocate a list of raw lightmaps */
1072 numRawSuperLuxels = 0;
1073 numRawLightmaps = 0;
1074 rawLightmaps = safe_malloc( numSurfsLightmapped * sizeof( *rawLightmaps ) );
1075 memset( rawLightmaps, 0, numSurfsLightmapped * sizeof( *rawLightmaps ) );
1076
1077 /* walk the list of sorted surfaces */
1078 for( i = 0; i < numBSPDrawSurfaces; i++ )
1079 {
1080 /* get info and attempt early out */
1081 num = sortSurfaces[ i ];
1082 ds = &bspDrawSurfaces[ num ];
1083 info = &surfaceInfos[ num ];
1084 if( info->hasLightmap == qfalse || info->lm != NULL || info->parentSurfaceNum >= 0 )
1085 continue;
1086
1087 /* allocate a new raw lightmap */
1088 lm = &rawLightmaps[ numRawLightmaps ];
1089 numRawLightmaps++;
1090
1091 /* set it up */
1092 lm->splotchFix = info->si->splotchFix;
1093 lm->firstLightSurface = numLightSurfaces;
1094 lm->numLightSurfaces = 0;
1095 lm->sampleSize = info->sampleSize;
1096 lm->actualSampleSize = info->sampleSize;
1097 lm->entityNum = info->entityNum;
1098 lm->recvShadows = info->recvShadows;
1099 lm->brightness = info->si->lmBrightness;
1100 lm->filterRadius = info->si->lmFilterRadius;
1101 VectorCopy( info->axis, lm->axis );
1102 lm->plane = info->plane;
1103 VectorCopy( info->mins, lm->mins );
1104 VectorCopy( info->maxs, lm->maxs );
1105
1106 lm->customWidth = info->si->lmCustomWidth;
1107 lm->customHeight = info->si->lmCustomHeight;
1108
1109 /* add the surface to the raw lightmap */
1110 AddSurfaceToRawLightmap( num, lm );
1111 info->lm = lm;
1112
1113 /* do an exhaustive merge */
1114 added = qtrue;
1115 while( added )
1116 {
1117 /* walk the list of surfaces again */
1118 added = qfalse;
1119 for( j = i + 1; j < numBSPDrawSurfaces && lm->finished == qfalse; j++ )
1120 {
1121 /* get info and attempt early out */
1122 num2 = sortSurfaces[ j ];
1123 ds2 = &bspDrawSurfaces[ num2 ];
1124 info2 = &surfaceInfos[ num2 ];
1125 if( info2->hasLightmap == qfalse || info2->lm != NULL )
1126 continue;
1127
1128 /* add the surface to the raw lightmap */
1129 if( AddSurfaceToRawLightmap( num2, lm ) )
1130 {
1131 info2->lm = lm;
1132 added = qtrue;
1133 }
1134 else
1135 {
1136 /* back up one */
1137 lm->numLightSurfaces--;
1138 numLightSurfaces--;
1139 }
1140 }
1141 }
1142
1143 /* finish the lightmap and allocate the various buffers */
1144 FinishRawLightmap( lm );
1145 }
1146
1147 /* allocate vertex luxel storage */
1148 for( k = 0; k < MAX_LIGHTMAPS; k++ )
1149 {
1150 vertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1151 memset( vertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1152 radVertexLuxels[ k ] = safe_malloc( numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1153 memset( radVertexLuxels[ k ], 0, numBSPDrawVerts * VERTEX_LUXEL_SIZE * sizeof( float ) );
1154 }
1155
1156 /* emit some stats */
1157 Sys_FPrintf( SYS_VRB, "%9d surfaces\n", numBSPDrawSurfaces );
1158 Sys_FPrintf( SYS_VRB, "%9d raw lightmaps\n", numRawLightmaps );
1159 Sys_FPrintf( SYS_VRB, "%9d surfaces vertex lit\n", numSurfsVertexLit );
1160 Sys_FPrintf( SYS_VRB, "%9d surfaces lightmapped\n", numSurfsLightmapped );
1161 Sys_FPrintf( SYS_VRB, "%9d planar surfaces lightmapped\n", numPlanarsLightmapped );
1162 Sys_FPrintf( SYS_VRB, "%9d non-planar surfaces lightmapped\n", numNonPlanarsLightmapped );
1163 Sys_FPrintf( SYS_VRB, "%9d patches lightmapped\n", numPatchesLightmapped );
1164 Sys_FPrintf( SYS_VRB, "%9d planar patches lightmapped\n", numPlanarPatchesLightmapped );
1165 }
1166
1167
1168
1169 /*
1170 StitchSurfaceLightmaps()
1171 stitches lightmap edges
1172 2002-11-20 update: use this func only for stitching nonplanar patch lightmap seams
1173 */
1174
1175 #define MAX_STITCH_CANDIDATES 32
1176 #define MAX_STITCH_LUXELS 64
1177
StitchSurfaceLightmaps(void)1178 void StitchSurfaceLightmaps( void )
1179 {
1180 int i, j, x, y, x2, y2, *cluster, *cluster2,
1181 numStitched, numCandidates, numLuxels, f, fOld, start;
1182 rawLightmap_t *lm, *a, *b, *c[ MAX_STITCH_CANDIDATES ];
1183 float *luxel, *luxel2, *origin, *origin2, *normal, *normal2,
1184 sampleSize, average[ 3 ], totalColor, ootc, *luxels[ MAX_STITCH_LUXELS ];
1185
1186
1187 /* disabled for now */
1188 return;
1189
1190 /* note it */
1191 Sys_Printf( "--- StitchSurfaceLightmaps ---\n");
1192
1193 /* init pacifier */
1194 fOld = -1;
1195 start = I_FloatTime();
1196
1197 /* walk the list of raw lightmaps */
1198 numStitched = 0;
1199 for( i = 0; i < numRawLightmaps; i++ )
1200 {
1201 /* print pacifier */
1202 f = 10 * i / numRawLightmaps;
1203 if( f != fOld )
1204 {
1205 fOld = f;
1206 Sys_Printf( "%i...", f );
1207 }
1208
1209 /* get lightmap a */
1210 a = &rawLightmaps[ i ];
1211
1212 /* walk rest of lightmaps */
1213 numCandidates = 0;
1214 for( j = i + 1; j < numRawLightmaps && numCandidates < MAX_STITCH_CANDIDATES; j++ )
1215 {
1216 /* get lightmap b */
1217 b = &rawLightmaps[ j ];
1218
1219 /* test bounding box */
1220 if( a->mins[ 0 ] > b->maxs[ 0 ] || a->maxs[ 0 ] < b->mins[ 0 ] ||
1221 a->mins[ 1 ] > b->maxs[ 1 ] || a->maxs[ 1 ] < b->mins[ 1 ] ||
1222 a->mins[ 2 ] > b->maxs[ 2 ] || a->maxs[ 2 ] < b->mins[ 2 ] )
1223 continue;
1224
1225 /* add candidate */
1226 c[ numCandidates++ ] = b;
1227 }
1228
1229 /* walk luxels */
1230 for( y = 0; y < a->sh; y++ )
1231 {
1232 for( x = 0; x < a->sw; x++ )
1233 {
1234 /* ignore unmapped/unlit luxels */
1235 lm = a;
1236 cluster = SUPER_CLUSTER( x, y );
1237 if( *cluster == CLUSTER_UNMAPPED )
1238 continue;
1239 luxel = SUPER_LUXEL( 0, x, y );
1240 if( luxel[ 3 ] <= 0.0f )
1241 continue;
1242
1243 /* get particulars */
1244 origin = SUPER_ORIGIN( x, y );
1245 normal = SUPER_NORMAL( x, y );
1246
1247 /* walk candidate list */
1248 for( j = 0; j < numCandidates; j++ )
1249 {
1250 /* get candidate */
1251 b = c[ j ];
1252 lm = b;
1253
1254 /* set samplesize to the smaller of the pair */
1255 sampleSize = 0.5f * (a->actualSampleSize < b->actualSampleSize ? a->actualSampleSize : b->actualSampleSize);
1256
1257 /* test bounding box */
1258 if( origin[ 0 ] < (b->mins[ 0 ] - sampleSize) || (origin[ 0 ] > b->maxs[ 0 ] + sampleSize) ||
1259 origin[ 1 ] < (b->mins[ 1 ] - sampleSize) || (origin[ 1 ] > b->maxs[ 1 ] + sampleSize) ||
1260 origin[ 2 ] < (b->mins[ 2 ] - sampleSize) || (origin[ 2 ] > b->maxs[ 2 ] + sampleSize) )
1261 continue;
1262
1263 /* walk candidate luxels */
1264 VectorClear( average );
1265 numLuxels = 0;
1266 totalColor = 0.0f;
1267 for( y2 = 0; y2 < b->sh && numLuxels < MAX_STITCH_LUXELS; y2++ )
1268 {
1269 for( x2 = 0; x2 < b->sw && numLuxels < MAX_STITCH_LUXELS; x2++ )
1270 {
1271 /* ignore same luxels */
1272 if( a == b && abs( x - x2 ) <= 1 && abs( y - y2 ) <= 1 )
1273 continue;
1274
1275 /* ignore unmapped/unlit luxels */
1276 cluster2 = SUPER_CLUSTER( x2, y2 );
1277 if( *cluster2 == CLUSTER_UNMAPPED )
1278 continue;
1279 luxel2 = SUPER_LUXEL( 0, x2, y2 );
1280 if( luxel2[ 3 ] <= 0.0f )
1281 continue;
1282
1283 /* get particulars */
1284 origin2 = SUPER_ORIGIN( x2, y2 );
1285 normal2 = SUPER_NORMAL( x2, y2 );
1286
1287 /* test normal */
1288 if( DotProduct( normal, normal2 ) < 0.5f )
1289 continue;
1290
1291 /* test bounds */
1292 if( fabs( origin[ 0 ] - origin2[ 0 ] ) > sampleSize ||
1293 fabs( origin[ 1 ] - origin2[ 1 ] ) > sampleSize ||
1294 fabs( origin[ 2 ] - origin2[ 2 ] ) > sampleSize )
1295 continue;
1296
1297 /* add luxel */
1298 //% VectorSet( luxel2, 255, 0, 255 );
1299 luxels[ numLuxels++ ] = luxel2;
1300 VectorAdd( average, luxel2, average );
1301 totalColor += luxel2[ 3 ];
1302 }
1303 }
1304
1305 /* early out */
1306 if( numLuxels == 0 )
1307 continue;
1308
1309 /* scale average */
1310 ootc = 1.0f / totalColor;
1311 VectorScale( average, ootc, luxel );
1312 luxel[ 3 ] = 1.0f;
1313 numStitched++;
1314 }
1315 }
1316 }
1317 }
1318
1319 /* emit statistics */
1320 Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
1321 Sys_FPrintf( SYS_VRB, "%9d luxels stitched\n", numStitched );
1322 }
1323
1324
1325
1326 /*
1327 CompareBSPLuxels()
1328 compares two surface lightmaps' bsp luxels, ignoring occluded luxels
1329 */
1330
1331 #define SOLID_EPSILON 0.0625
1332 #define LUXEL_TOLERANCE 0.0025
1333 #define LUXEL_COLOR_FRAC 0.001302083 /* 1 / 3 / 256 */
1334
CompareBSPLuxels(rawLightmap_t * a,int aNum,rawLightmap_t * b,int bNum)1335 static qboolean CompareBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1336 {
1337 rawLightmap_t *lm;
1338 int x, y;
1339 double delta, total, rd, gd, bd;
1340 float *aLuxel, *bLuxel;
1341
1342
1343 /* styled lightmaps will never be collapsed to non-styled lightmaps when there is _minlight */
1344 if( (minLight[ 0 ] || minLight[ 1 ] || minLight[ 2 ]) &&
1345 ((aNum == 0 && bNum != 0) || (aNum != 0 && bNum == 0)) )
1346 return qfalse;
1347
1348 /* basic tests */
1349 if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1350 a->brightness != b->brightness ||
1351 a->solid[ aNum ] != b->solid[ bNum ] ||
1352 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1353 return qfalse;
1354
1355 /* compare solid color lightmaps */
1356 if( a->solid[ aNum ] && b->solid[ bNum ] )
1357 {
1358 /* get deltas */
1359 rd = fabs( a->solidColor[ aNum ][ 0 ] - b->solidColor[ bNum ][ 0 ] );
1360 gd = fabs( a->solidColor[ aNum ][ 1 ] - b->solidColor[ bNum ][ 1 ] );
1361 bd = fabs( a->solidColor[ aNum ][ 2 ] - b->solidColor[ bNum ][ 2 ] );
1362
1363 /* compare color */
1364 if( rd > SOLID_EPSILON || gd > SOLID_EPSILON|| bd > SOLID_EPSILON )
1365 return qfalse;
1366
1367 /* okay */
1368 return qtrue;
1369 }
1370
1371 /* compare nonsolid lightmaps */
1372 if( a->w != b->w || a->h != b->h )
1373 return qfalse;
1374
1375 /* compare luxels */
1376 delta = 0.0;
1377 total = 0.0;
1378 for( y = 0; y < a->h; y++ )
1379 {
1380 for( x = 0; x < a->w; x++ )
1381 {
1382 /* increment total */
1383 total += 1.0;
1384
1385 /* get luxels */
1386 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1387 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1388
1389 /* ignore unused luxels */
1390 if( aLuxel[ 0 ] < 0 || bLuxel[ 0 ] < 0 )
1391 continue;
1392
1393 /* get deltas */
1394 rd = fabs( aLuxel[ 0 ] - bLuxel[ 0 ] );
1395 gd = fabs( aLuxel[ 1 ] - bLuxel[ 1 ] );
1396 bd = fabs( aLuxel[ 2 ] - bLuxel[ 2 ] );
1397
1398 /* 2003-09-27: compare individual luxels */
1399 if( rd > 3.0 || gd > 3.0 || bd > 3.0 )
1400 return qfalse;
1401
1402 /* compare (fixme: take into account perceptual differences) */
1403 delta += rd * LUXEL_COLOR_FRAC;
1404 delta += gd * LUXEL_COLOR_FRAC;
1405 delta += bd * LUXEL_COLOR_FRAC;
1406
1407 /* is the change too high? */
1408 if( total > 0.0 && ((delta / total) > LUXEL_TOLERANCE) )
1409 return qfalse;
1410 }
1411 }
1412
1413 /* made it this far, they must be identical (or close enough) */
1414 return qtrue;
1415 }
1416
1417
1418
1419 /*
1420 MergeBSPLuxels()
1421 merges two surface lightmaps' bsp luxels, overwriting occluded luxels
1422 */
1423
MergeBSPLuxels(rawLightmap_t * a,int aNum,rawLightmap_t * b,int bNum)1424 static qboolean MergeBSPLuxels( rawLightmap_t *a, int aNum, rawLightmap_t *b, int bNum )
1425 {
1426 rawLightmap_t *lm;
1427 int x, y;
1428 float luxel[ 3 ], *aLuxel, *bLuxel;
1429
1430
1431 /* basic tests */
1432 if( a->customWidth != b->customWidth || a->customHeight != b->customHeight ||
1433 a->brightness != b->brightness ||
1434 a->solid[ aNum ] != b->solid[ bNum ] ||
1435 a->bspLuxels[ aNum ] == NULL || b->bspLuxels[ bNum ] == NULL )
1436 return qfalse;
1437
1438 /* compare solid lightmaps */
1439 if( a->solid[ aNum ] && b->solid[ bNum ] )
1440 {
1441 /* average */
1442 VectorAdd( a->solidColor[ aNum ], b->solidColor[ bNum ], luxel );
1443 VectorScale( luxel, 0.5f, luxel );
1444
1445 /* copy to both */
1446 VectorCopy( luxel, a->solidColor[ aNum ] );
1447 VectorCopy( luxel, b->solidColor[ bNum ] );
1448
1449 /* return to sender */
1450 return qtrue;
1451 }
1452
1453 /* compare nonsolid lightmaps */
1454 if( a->w != b->w || a->h != b->h )
1455 return qfalse;
1456
1457 /* merge luxels */
1458 for( y = 0; y < a->h; y++ )
1459 {
1460 for( x = 0; x < a->w; x++ )
1461 {
1462 /* get luxels */
1463 lm = a; aLuxel = BSP_LUXEL( aNum, x, y );
1464 lm = b; bLuxel = BSP_LUXEL( bNum, x, y );
1465
1466 /* handle occlusion mismatch */
1467 if( aLuxel[ 0 ] < 0.0f )
1468 VectorCopy( bLuxel, aLuxel );
1469 else if( bLuxel[ 0 ] < 0.0f )
1470 VectorCopy( aLuxel, bLuxel );
1471 else
1472 {
1473 /* average */
1474 VectorAdd( aLuxel, bLuxel, luxel );
1475 VectorScale( luxel, 0.5f, luxel );
1476
1477 /* debugging code */
1478 //% luxel[ 2 ] += 64.0f;
1479
1480 /* copy to both */
1481 VectorCopy( luxel, aLuxel );
1482 VectorCopy( luxel, bLuxel );
1483 }
1484 }
1485 }
1486
1487 /* done */
1488 return qtrue;
1489 }
1490
1491
1492
1493 /*
1494 ApproximateLuxel()
1495 determines if a single luxel is can be approximated with the interpolated vertex rgba
1496 */
1497
ApproximateLuxel(rawLightmap_t * lm,bspDrawVert_t * dv)1498 static qboolean ApproximateLuxel( rawLightmap_t *lm, bspDrawVert_t *dv )
1499 {
1500 int i, x, y, d, lightmapNum;
1501 float *luxel;
1502 vec3_t color, vertexColor;
1503 byte cb[ 4 ], vcb[ 4 ];
1504
1505
1506 /* find luxel xy coords */
1507 x = dv->lightmap[ 0 ][ 0 ] / superSample;
1508 y = dv->lightmap[ 0 ][ 1 ] / superSample;
1509 if( x < 0 )
1510 x = 0;
1511 else if( x >= lm->w )
1512 x = lm->w - 1;
1513 if( y < 0 )
1514 y = 0;
1515 else if( y >= lm->h )
1516 y = lm->h - 1;
1517
1518 /* walk list */
1519 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1520 {
1521 /* early out */
1522 if( lm->styles[ lightmapNum ] == LS_NONE )
1523 continue;
1524
1525 /* get luxel */
1526 luxel = BSP_LUXEL( lightmapNum, x, y );
1527
1528 /* ignore occluded luxels */
1529 if( luxel[ 0 ] < 0.0f || luxel[ 1 ] < 0.0f || luxel[ 2 ] < 0.0f )
1530 return qtrue;
1531
1532 /* copy, set min color and compare */
1533 VectorCopy( luxel, color );
1534 VectorCopy( dv->color[ 0 ], vertexColor );
1535
1536 /* styles are not affected by minlight */
1537 if( lightmapNum == 0 )
1538 {
1539 for( i = 0; i < 3; i++ )
1540 {
1541 /* set min color */
1542 if( color[ i ] < minLight[ i ] )
1543 color[ i ] = minLight[ i ];
1544 if( vertexColor[ i ] < minLight[ i ] ) /* note NOT minVertexLight */
1545 vertexColor[ i ] = minLight[ i ];
1546 }
1547 }
1548
1549 /* set to bytes */
1550 ColorToBytes( color, cb, 1.0f );
1551 ColorToBytes( vertexColor, vcb, 1.0f );
1552
1553 /* compare */
1554 for( i = 0; i < 3; i++ )
1555 {
1556 d = cb[ i ] - vcb[ i ];
1557 if( d < 0 )
1558 d *= -1;
1559 if( d > approximateTolerance )
1560 return qfalse;
1561 }
1562 }
1563
1564 /* close enough for the girls i date */
1565 return qtrue;
1566 }
1567
1568
1569
1570 /*
1571 ApproximateTriangle()
1572 determines if a single triangle can be approximated with vertex rgba
1573 */
1574
ApproximateTriangle_r(rawLightmap_t * lm,bspDrawVert_t * dv[3])1575 static qboolean ApproximateTriangle_r( rawLightmap_t *lm, bspDrawVert_t *dv[ 3 ] )
1576 {
1577 bspDrawVert_t mid, *dv2[ 3 ];
1578 int max;
1579
1580
1581 /* approximate the vertexes */
1582 if( ApproximateLuxel( lm, dv[ 0 ] ) == qfalse )
1583 return qfalse;
1584 if( ApproximateLuxel( lm, dv[ 1 ] ) == qfalse )
1585 return qfalse;
1586 if( ApproximateLuxel( lm, dv[ 2 ] ) == qfalse )
1587 return qfalse;
1588
1589 /* subdivide calc */
1590 {
1591 int i;
1592 float dx, dy, dist, maxDist;
1593
1594
1595 /* find the longest edge and split it */
1596 max = -1;
1597 maxDist = 0;
1598 for( i = 0; i < 3; i++ )
1599 {
1600 dx = dv[ i ]->lightmap[ 0 ][ 0 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 0 ];
1601 dy = dv[ i ]->lightmap[ 0 ][ 1 ] - dv[ (i + 1) % 3 ]->lightmap[ 0 ][ 1 ];
1602 dist = sqrt( (dx * dx) + (dy * dy) );
1603 if( dist > maxDist )
1604 {
1605 maxDist = dist;
1606 max = i;
1607 }
1608 }
1609
1610 /* try to early out */
1611 if( i < 0 || maxDist < subdivideThreshold )
1612 return qtrue;
1613 }
1614
1615 /* split the longest edge and map it */
1616 LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
1617 if( ApproximateLuxel( lm, &mid ) == qfalse )
1618 return qfalse;
1619
1620 /* recurse to first triangle */
1621 VectorCopy( dv, dv2 );
1622 dv2[ max ] = ∣
1623 if( ApproximateTriangle_r( lm, dv2 ) == qfalse )
1624 return qfalse;
1625
1626 /* recurse to second triangle */
1627 VectorCopy( dv, dv2 );
1628 dv2[ (max + 1) % 3 ] = ∣
1629 return ApproximateTriangle_r( lm, dv2 );
1630 }
1631
1632
1633
1634 /*
1635 ApproximateLightmap()
1636 determines if a raw lightmap can be approximated sufficiently with vertex colors
1637 */
1638
ApproximateLightmap(rawLightmap_t * lm)1639 static qboolean ApproximateLightmap( rawLightmap_t *lm )
1640 {
1641 int n, num, i, x, y, pw[ 5 ], r;
1642 bspDrawSurface_t *ds;
1643 surfaceInfo_t *info;
1644 mesh_t src, *subdivided, *mesh;
1645 bspDrawVert_t *verts, *dv[ 3 ];
1646 qboolean approximated;
1647
1648
1649 /* approximating? */
1650 if( approximateTolerance <= 0 )
1651 return qfalse;
1652
1653 /* test for jmonroe */
1654 #if 0
1655 /* don't approx lightmaps with styled twins */
1656 if( lm->numStyledTwins > 0 )
1657 return qfalse;
1658
1659 /* don't approx lightmaps with styles */
1660 for( i = 1; i < MAX_LIGHTMAPS; i++ )
1661 {
1662 if( lm->styles[ i ] != LS_NONE )
1663 return qfalse;
1664 }
1665 #endif
1666
1667 /* assume reduced until shadow detail is found */
1668 approximated = qtrue;
1669
1670 /* walk the list of surfaces on this raw lightmap */
1671 for( n = 0; n < lm->numLightSurfaces; n++ )
1672 {
1673 /* get surface */
1674 num = lightSurfaces[ lm->firstLightSurface + n ];
1675 ds = &bspDrawSurfaces[ num ];
1676 info = &surfaceInfos[ num ];
1677
1678 /* assume not-reduced initially */
1679 info->approximated = qfalse;
1680
1681 /* bail if lightmap doesn't match up */
1682 if( info->lm != lm )
1683 continue;
1684
1685 /* bail if not vertex lit */
1686 if( info->si->noVertexLight )
1687 continue;
1688
1689 /* assume that surfaces whose bounding boxes is smaller than 2x samplesize will be forced to vertex */
1690 if( (info->maxs[ 0 ] - info->mins[ 0 ]) <= (2.0f * info->sampleSize) &&
1691 (info->maxs[ 1 ] - info->mins[ 1 ]) <= (2.0f * info->sampleSize) &&
1692 (info->maxs[ 2 ] - info->mins[ 2 ]) <= (2.0f * info->sampleSize) )
1693 {
1694 info->approximated = qtrue;
1695 numSurfsVertexForced++;
1696 continue;
1697 }
1698
1699 /* handle the triangles */
1700 switch( ds->surfaceType )
1701 {
1702 case MST_PLANAR:
1703 /* get verts */
1704 verts = yDrawVerts + ds->firstVert;
1705
1706 /* map the triangles */
1707 info->approximated = qtrue;
1708 for( i = 0; i < ds->numIndexes && info->approximated; i += 3 )
1709 {
1710 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1711 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1712 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1713 info->approximated = ApproximateTriangle_r( lm, dv );
1714 }
1715 break;
1716
1717 case MST_PATCH:
1718 /* make a mesh from the drawsurf */
1719 src.width = ds->patchWidth;
1720 src.height = ds->patchHeight;
1721 src.verts = &yDrawVerts[ ds->firstVert ];
1722 //% subdivided = SubdivideMesh( src, 8, 512 );
1723 subdivided = SubdivideMesh2( src, info->patchIterations );
1724
1725 /* fit it to the curve and remove colinear verts on rows/columns */
1726 PutMeshOnCurve( *subdivided );
1727 mesh = RemoveLinearMeshColumnsRows( subdivided );
1728 FreeMesh( subdivided );
1729
1730 /* get verts */
1731 verts = mesh->verts;
1732
1733 /* map the mesh quads */
1734 info->approximated = qtrue;
1735 for( y = 0; y < (mesh->height - 1) && info->approximated; y++ )
1736 {
1737 for( x = 0; x < (mesh->width - 1) && info->approximated; x++ )
1738 {
1739 /* set indexes */
1740 pw[ 0 ] = x + (y * mesh->width);
1741 pw[ 1 ] = x + ((y + 1) * mesh->width);
1742 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1743 pw[ 3 ] = x + 1 + (y * mesh->width);
1744 pw[ 4 ] = x + (y * mesh->width); /* same as pw[ 0 ] */
1745
1746 /* set radix */
1747 r = (x + y) & 1;
1748
1749 /* get drawverts and map first triangle */
1750 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1751 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1752 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1753 info->approximated = ApproximateTriangle_r( lm, dv );
1754
1755 /* get drawverts and map second triangle */
1756 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1757 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1758 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1759 if( info->approximated )
1760 info->approximated = ApproximateTriangle_r( lm, dv );
1761 }
1762 }
1763
1764 /* free the mesh */
1765 FreeMesh( mesh );
1766 break;
1767
1768 default:
1769 break;
1770 }
1771
1772 /* reduced? */
1773 if( info->approximated == qfalse )
1774 approximated = qfalse;
1775 else
1776 numSurfsVertexApproximated++;
1777 }
1778
1779 /* return */
1780 return approximated;
1781 }
1782
1783
1784
1785 /*
1786 TestOutLightmapStamp()
1787 tests a stamp on a given lightmap for validity
1788 */
1789
TestOutLightmapStamp(rawLightmap_t * lm,int lightmapNum,outLightmap_t * olm,int x,int y)1790 static qboolean TestOutLightmapStamp( rawLightmap_t *lm, int lightmapNum, outLightmap_t *olm, int x, int y )
1791 {
1792 int sx, sy, ox, oy, offset;
1793 float *luxel;
1794
1795
1796 /* bounds check */
1797 if( x < 0 || y < 0 || (x + lm->w) > olm->customWidth || (y + lm->h) > olm->customHeight )
1798 return qfalse;
1799
1800 /* solid lightmaps test a 1x1 stamp */
1801 if( lm->solid[ lightmapNum ] )
1802 {
1803 offset = (y * olm->customWidth) + x;
1804 if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1805 return qfalse;
1806 return qtrue;
1807 }
1808
1809 /* test the stamp */
1810 for( sy = 0; sy < lm->h; sy++ )
1811 {
1812 for( sx = 0; sx < lm->w; sx++ )
1813 {
1814 /* get luxel */
1815 luxel = BSP_LUXEL( lightmapNum, sx, sy );
1816 if( luxel[ 0 ] < 0.0f )
1817 continue;
1818
1819 /* get bsp lightmap coords and test */
1820 ox = x + sx;
1821 oy = y + sy;
1822 offset = (oy * olm->customWidth) + ox;
1823 if( olm->lightBits[ offset >> 3 ] & (1 << (offset & 7)) )
1824 return qfalse;
1825 }
1826 }
1827
1828 /* stamp is empty */
1829 return qtrue;
1830 }
1831
1832
1833
1834 /*
1835 SetupOutLightmap()
1836 sets up an output lightmap
1837 */
1838
SetupOutLightmap(rawLightmap_t * lm,outLightmap_t * olm)1839 static void SetupOutLightmap( rawLightmap_t *lm, outLightmap_t *olm )
1840 {
1841 /* dummy check */
1842 if( lm == NULL || olm == NULL )
1843 return;
1844
1845 /* is this a "normal" bsp-stored lightmap? */
1846 if( (lm->customWidth == game->lightmapSize && lm->customHeight == game->lightmapSize) || externalLightmaps )
1847 {
1848 olm->lightmapNum = numBSPLightmaps;
1849 numBSPLightmaps++;
1850
1851 /* lightmaps are interleaved with light direction maps */
1852 if( deluxemap )
1853 numBSPLightmaps++;
1854 }
1855 else
1856 olm->lightmapNum = -3;
1857
1858 /* set external lightmap number */
1859 olm->extLightmapNum = -1;
1860
1861 /* set it up */
1862 olm->numLightmaps = 0;
1863 olm->customWidth = lm->customWidth;
1864 olm->customHeight = lm->customHeight;
1865 olm->freeLuxels = olm->customWidth * olm->customHeight;
1866 olm->numShaders = 0;
1867
1868 /* allocate buffers */
1869 olm->lightBits = safe_malloc( (olm->customWidth * olm->customHeight / 8) + 8 );
1870 memset( olm->lightBits, 0, (olm->customWidth * olm->customHeight / 8) + 8 );
1871 olm->bspLightBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1872 memset( olm->bspLightBytes, 0, olm->customWidth * olm->customHeight * 3 );
1873 if( deluxemap )
1874 {
1875 olm->bspDirBytes = safe_malloc( olm->customWidth * olm->customHeight * 3 );
1876 memset( olm->bspDirBytes, 0, olm->customWidth * olm->customHeight * 3 );
1877 }
1878 }
1879
1880
1881
1882 /*
1883 FindOutLightmaps()
1884 for a given surface lightmap, find output lightmap pages and positions for it
1885 */
1886
FindOutLightmaps(rawLightmap_t * lm)1887 static void FindOutLightmaps( rawLightmap_t *lm )
1888 {
1889 int i, j, lightmapNum, xMax, yMax, x, y, sx, sy, ox, oy, offset, temp;
1890 outLightmap_t *olm;
1891 surfaceInfo_t *info;
1892 float *luxel, *deluxel;
1893 vec3_t color, direction;
1894 byte *pixel;
1895 qboolean ok;
1896
1897
1898 /* set default lightmap number (-3 = LIGHTMAP_BY_VERTEX) */
1899 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1900 lm->outLightmapNums[ lightmapNum ] = -3;
1901
1902 /* can this lightmap be approximated with vertex color? */
1903 if( ApproximateLightmap( lm ) )
1904 return;
1905
1906 /* walk list */
1907 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1908 {
1909 /* early out */
1910 if( lm->styles[ lightmapNum ] == LS_NONE )
1911 continue;
1912
1913 /* don't store twinned lightmaps */
1914 if( lm->twins[ lightmapNum ] != NULL )
1915 continue;
1916
1917 /* if this is a styled lightmap, try some normalized locations first */
1918 ok = qfalse;
1919 if( lightmapNum > 0 && outLightmaps != NULL )
1920 {
1921 /* loop twice */
1922 for( j = 0; j < 2; j++ )
1923 {
1924 /* try identical position */
1925 for( i = 0; i < numOutLightmaps; i++ )
1926 {
1927 /* get the output lightmap */
1928 olm = &outLightmaps[ i ];
1929
1930 /* simple early out test */
1931 if( olm->freeLuxels < lm->used )
1932 continue;
1933
1934 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
1935 if( olm->customWidth != lm->customWidth ||
1936 olm->customHeight != lm->customHeight )
1937 continue;
1938
1939 /* try identical */
1940 if( j == 0 )
1941 {
1942 x = lm->lightmapX[ 0 ];
1943 y = lm->lightmapY[ 0 ];
1944 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1945 }
1946
1947 /* try shifting */
1948 else
1949 {
1950 for( sy = -1; sy <= 1; sy++ )
1951 {
1952 for( sx = -1; sx <= 1; sx++ )
1953 {
1954 x = lm->lightmapX[ 0 ] + sx * (olm->customWidth >> 1); //% lm->w;
1955 y = lm->lightmapY[ 0 ] + sy * (olm->customHeight >> 1); //% lm->h;
1956 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
1957
1958 if( ok )
1959 break;
1960 }
1961
1962 if( ok )
1963 break;
1964 }
1965 }
1966
1967 if( ok )
1968 break;
1969 }
1970
1971 if( ok )
1972 break;
1973 }
1974 }
1975
1976 /* try normal placement algorithm */
1977 if( ok == qfalse )
1978 {
1979 /* reset origin */
1980 x = 0;
1981 y = 0;
1982
1983 /* walk the list of lightmap pages */
1984 for( i = 0; i < numOutLightmaps; i++ )
1985 {
1986 /* get the output lightmap */
1987 olm = &outLightmaps[ i ];
1988
1989 /* simple early out test */
1990 if( olm->freeLuxels < lm->used )
1991 continue;
1992
1993 /* don't store non-custom raw lightmaps on custom bsp lightmaps */
1994 if( olm->customWidth != lm->customWidth ||
1995 olm->customHeight != lm->customHeight )
1996 continue;
1997
1998 /* set maxs */
1999 if( lm->solid[ lightmapNum ] )
2000 {
2001 xMax = olm->customWidth;
2002 yMax = olm->customHeight;
2003 }
2004 else
2005 {
2006 xMax = (olm->customWidth - lm->w) + 1;
2007 yMax = (olm->customHeight - lm->h) + 1;
2008 }
2009
2010 /* walk the origin around the lightmap */
2011 for( y = 0; y < yMax; y++ )
2012 {
2013 for( x = 0; x < xMax; x++ )
2014 {
2015 /* find a fine tract of lauhnd */
2016 ok = TestOutLightmapStamp( lm, lightmapNum, olm, x, y );
2017
2018 if( ok )
2019 break;
2020 }
2021
2022 if( ok )
2023 break;
2024 }
2025
2026 if( ok )
2027 break;
2028
2029 /* reset x and y */
2030 x = 0;
2031 y = 0;
2032 }
2033 }
2034
2035 /* no match? */
2036 if( ok == qfalse )
2037 {
2038 /* allocate two new output lightmaps */
2039 numOutLightmaps += 2;
2040 olm = safe_malloc( numOutLightmaps * sizeof( outLightmap_t ) );
2041 if( outLightmaps != NULL && numOutLightmaps > 2 )
2042 {
2043 memcpy( olm, outLightmaps, (numOutLightmaps - 2) * sizeof( outLightmap_t ) );
2044 free( outLightmaps );
2045 }
2046 outLightmaps = olm;
2047
2048 /* initialize both out lightmaps */
2049 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 2 ] );
2050 SetupOutLightmap( lm, &outLightmaps[ numOutLightmaps - 1 ] );
2051
2052 /* set out lightmap */
2053 i = numOutLightmaps - 2;
2054 olm = &outLightmaps[ i ];
2055
2056 /* set stamp xy origin to the first surface lightmap */
2057 if( lightmapNum > 0 )
2058 {
2059 x = lm->lightmapX[ 0 ];
2060 y = lm->lightmapY[ 0 ];
2061 }
2062 }
2063
2064 /* if this is a style-using lightmap, it must be exported */
2065 if( lightmapNum > 0 && game->load != LoadRBSPFile )
2066 olm->extLightmapNum = 0;
2067
2068 /* add the surface lightmap to the bsp lightmap */
2069 lm->outLightmapNums[ lightmapNum ] = i;
2070 lm->lightmapX[ lightmapNum ] = x;
2071 lm->lightmapY[ lightmapNum ] = y;
2072 olm->numLightmaps++;
2073
2074 /* add shaders */
2075 for( i = 0; i < lm->numLightSurfaces; i++ )
2076 {
2077 /* get surface info */
2078 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + i ] ];
2079
2080 /* test for shader */
2081 for( j = 0; j < olm->numShaders; j++ )
2082 {
2083 if( olm->shaders[ j ] == info->si )
2084 break;
2085 }
2086
2087 /* if it doesn't exist, add it */
2088 if( j >= olm->numShaders && olm->numShaders < MAX_LIGHTMAP_SHADERS )
2089 {
2090 olm->shaders[ olm->numShaders ] = info->si;
2091 olm->numShaders++;
2092 numLightmapShaders++;
2093 }
2094 }
2095
2096 /* set maxs */
2097 if( lm->solid[ lightmapNum ] )
2098 {
2099 xMax = 1;
2100 yMax = 1;
2101 }
2102 else
2103 {
2104 xMax = lm->w;
2105 yMax = lm->h;
2106 }
2107
2108 /* mark the bits used */
2109 for( y = 0; y < yMax; y++ )
2110 {
2111 for( x = 0; x < xMax; x++ )
2112 {
2113 /* get luxel */
2114 luxel = BSP_LUXEL( lightmapNum, x, y );
2115 deluxel = BSP_DELUXEL( x, y );
2116 if( luxel[ 0 ] < 0.0f && !lm->solid[ lightmapNum ])
2117 continue;
2118
2119 /* set minimum light */
2120 if( lm->solid[ lightmapNum ] )
2121 {
2122 if( debug )
2123 VectorSet( color, 255.0f, 0.0f, 0.0f );
2124 else
2125 VectorCopy( lm->solidColor[ lightmapNum ], color );
2126 }
2127 else
2128 VectorCopy( luxel, color );
2129
2130 /* styles are not affected by minlight */
2131 if( lightmapNum == 0 )
2132 {
2133 for( i = 0; i < 3; i++ )
2134 {
2135 if( color[ i ] < minLight[ i ] )
2136 color[ i ] = minLight[ i ];
2137 }
2138 }
2139
2140 /* get bsp lightmap coords */
2141 ox = x + lm->lightmapX[ lightmapNum ];
2142 oy = y + lm->lightmapY[ lightmapNum ];
2143 offset = (oy * olm->customWidth) + ox;
2144
2145 /* flag pixel as used */
2146 olm->lightBits[ offset >> 3 ] |= (1 << (offset & 7));
2147 olm->freeLuxels--;
2148
2149 /* store color */
2150 pixel = olm->bspLightBytes + (((oy * olm->customWidth) + ox) * 3);
2151 ColorToBytes( color, pixel, lm->brightness );
2152
2153 /* store direction */
2154 if( deluxemap )
2155 {
2156 /* normalize average light direction */
2157 if( VectorNormalize( deluxel, direction ) )
2158 {
2159 /* encode [-1,1] in [0,255] */
2160 pixel = olm->bspDirBytes + (((oy * olm->customWidth) + ox) * 3);
2161 for( i = 0; i < 3; i++ )
2162 {
2163 temp = (direction[ i ] + 1.0f) * 127.5f;
2164 if( temp < 0 )
2165 pixel[ i ] = 0;
2166 else if( temp > 255 )
2167 pixel[ i ] = 255;
2168 else
2169 pixel[ i ] = temp;
2170 }
2171 }
2172 }
2173 }
2174 }
2175 }
2176 }
2177
2178
2179
2180 /*
2181 CompareRawLightmap()
2182 compare function for qsort()
2183 */
2184
CompareRawLightmap(const void * a,const void * b)2185 static int CompareRawLightmap( const void *a, const void *b )
2186 {
2187 rawLightmap_t *alm, *blm;
2188 surfaceInfo_t *aInfo, *bInfo;
2189 int i, min, diff;
2190
2191
2192 /* get lightmaps */
2193 alm = &rawLightmaps[ *((int*) a) ];
2194 blm = &rawLightmaps[ *((int*) b) ];
2195
2196 /* get min number of surfaces */
2197 min = (alm->numLightSurfaces < blm->numLightSurfaces ? alm->numLightSurfaces : blm->numLightSurfaces);
2198
2199 /* iterate */
2200 for( i = 0; i < min; i++ )
2201 {
2202 /* get surface info */
2203 aInfo = &surfaceInfos[ lightSurfaces[ alm->firstLightSurface + i ] ];
2204 bInfo = &surfaceInfos[ lightSurfaces[ blm->firstLightSurface + i ] ];
2205
2206 /* compare shader names */
2207 diff = strcmp( aInfo->si->shader, bInfo->si->shader );
2208 if( diff != 0 )
2209 return diff;
2210 }
2211
2212 /* test style count */
2213 diff = 0;
2214 for( i = 0; i < MAX_LIGHTMAPS; i++ )
2215 diff += blm->styles[ i ] - alm->styles[ i ];
2216 if( diff )
2217 return diff;
2218
2219 /* compare size */
2220 diff = (blm->w * blm->h) - (alm->w * alm->h);
2221 if( diff != 0 )
2222 return diff;
2223
2224 /* must be equivalent */
2225 return 0;
2226 }
2227
2228
2229
2230 /*
2231 StoreSurfaceLightmaps()
2232 stores the surface lightmaps into the bsp as byte rgb triplets
2233 */
2234
StoreSurfaceLightmaps(void)2235 void StoreSurfaceLightmaps( void )
2236 {
2237 int i, j, k, x, y, lx, ly, sx, sy, *cluster, mappedSamples;
2238 int style, size, lightmapNum, lightmapNum2;
2239 float *normal, *luxel, *bspLuxel, *bspLuxel2, *radLuxel, samples, occludedSamples;
2240 vec3_t sample, occludedSample, dirSample, colorMins, colorMaxs;
2241 float *deluxel, *bspDeluxel, *bspDeluxel2;
2242 byte *lb;
2243 int numUsed, numTwins, numTwinLuxels, numStored;
2244 float lmx, lmy, efficiency;
2245 vec3_t color;
2246 bspDrawSurface_t *ds, *parent, dsTemp;
2247 surfaceInfo_t *info;
2248 rawLightmap_t *lm, *lm2;
2249 outLightmap_t *olm;
2250 bspDrawVert_t *dv, *ydv, *dvParent;
2251 char dirname[ 1024 ], filename[ 1024 ];
2252 shaderInfo_t *csi;
2253 char lightmapName[ 128 ];
2254 char *rgbGenValues[ 256 ];
2255 char *alphaGenValues[ 256 ];
2256
2257
2258 /* note it */
2259 Sys_Printf( "--- StoreSurfaceLightmaps ---\n");
2260
2261 /* setup */
2262 strcpy( dirname, source );
2263 StripExtension( dirname );
2264 memset( rgbGenValues, 0, sizeof( rgbGenValues ) );
2265 memset( alphaGenValues, 0, sizeof( alphaGenValues ) );
2266
2267 /* -----------------------------------------------------------------
2268 average the sampled luxels into the bsp luxels
2269 ----------------------------------------------------------------- */
2270
2271 /* note it */
2272 Sys_FPrintf( SYS_VRB, "Subsampling..." );
2273
2274 /* walk the list of raw lightmaps */
2275 numUsed = 0;
2276 numTwins = 0;
2277 numTwinLuxels = 0;
2278 numSolidLightmaps = 0;
2279 for( i = 0; i < numRawLightmaps; i++ )
2280 {
2281 /* get lightmap */
2282 lm = &rawLightmaps[ i ];
2283
2284 /* walk individual lightmaps */
2285 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2286 {
2287 /* early outs */
2288 if( lm->superLuxels[ lightmapNum ] == NULL )
2289 continue;
2290
2291 /* allocate bsp luxel storage */
2292 if( lm->bspLuxels[ lightmapNum ] == NULL )
2293 {
2294 size = lm->w * lm->h * BSP_LUXEL_SIZE * sizeof( float );
2295 lm->bspLuxels[ lightmapNum ] = safe_malloc( size );
2296 memset( lm->bspLuxels[ lightmapNum ], 0, size );
2297 }
2298
2299 /* allocate radiosity lightmap storage */
2300 if( bounce )
2301 {
2302 size = lm->w * lm->h * RAD_LUXEL_SIZE * sizeof( float );
2303 if( lm->radLuxels[ lightmapNum ] == NULL )
2304 lm->radLuxels[ lightmapNum ] = safe_malloc( size );
2305 memset( lm->radLuxels[ lightmapNum ], 0, size );
2306 }
2307
2308 /* average supersampled luxels */
2309 for( y = 0; y < lm->h; y++ )
2310 {
2311 for( x = 0; x < lm->w; x++ )
2312 {
2313 /* subsample */
2314 samples = 0.0f;
2315 occludedSamples = 0.0f;
2316 mappedSamples = 0;
2317 VectorClear( sample );
2318 VectorClear( occludedSample );
2319 VectorClear( dirSample );
2320 for( ly = 0; ly < superSample; ly++ )
2321 {
2322 for( lx = 0; lx < superSample; lx++ )
2323 {
2324 /* sample luxel */
2325 sx = x * superSample + lx;
2326 sy = y * superSample + ly;
2327 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2328 deluxel = SUPER_DELUXEL( sx, sy );
2329 normal = SUPER_NORMAL( sx, sy );
2330 cluster = SUPER_CLUSTER( sx, sy );
2331
2332 /* sample deluxemap */
2333 if( deluxemap && lightmapNum == 0 )
2334 VectorAdd( dirSample, deluxel, dirSample );
2335
2336 /* keep track of used/occluded samples */
2337 if( *cluster != CLUSTER_UNMAPPED )
2338 mappedSamples++;
2339
2340 /* handle lightmap border? */
2341 if( lightmapBorder && (sx == 0 || sx == (lm->sw - 1) || sy == 0 || sy == (lm->sh - 1) ) && luxel[ 3 ] > 0.0f )
2342 {
2343 VectorSet( sample, 255.0f, 0.0f, 0.0f );
2344 samples += 1.0f;
2345 }
2346
2347 /* handle debug */
2348 else if( debug && *cluster < 0 )
2349 {
2350 if( *cluster == CLUSTER_UNMAPPED )
2351 VectorSet( luxel, 255, 204, 0 );
2352 else if( *cluster == CLUSTER_OCCLUDED )
2353 VectorSet( luxel, 255, 0, 255 );
2354 else if( *cluster == CLUSTER_FLOODED )
2355 VectorSet( luxel, 0, 32, 255 );
2356 VectorAdd( occludedSample, luxel, occludedSample );
2357 occludedSamples += 1.0f;
2358 }
2359
2360 /* normal luxel handling */
2361 else if( luxel[ 3 ] > 0.0f )
2362 {
2363 /* handle lit or flooded luxels */
2364 if( *cluster > 0 || *cluster == CLUSTER_FLOODED )
2365 {
2366 VectorAdd( sample, luxel, sample );
2367 samples += luxel[ 3 ];
2368 }
2369
2370 /* handle occluded or unmapped luxels */
2371 else
2372 {
2373 VectorAdd( occludedSample, luxel, occludedSample );
2374 occludedSamples += luxel[ 3 ];
2375 }
2376
2377 /* handle style debugging */
2378 if( debug && lightmapNum > 0 && x < 2 && y < 2 )
2379 {
2380 VectorCopy( debugColors[ 0 ], sample );
2381 samples = 1;
2382 }
2383 }
2384 }
2385 }
2386
2387 /* only use occluded samples if necessary */
2388 if( samples <= 0.0f )
2389 {
2390 VectorCopy( occludedSample, sample );
2391 samples = occludedSamples;
2392 }
2393
2394 /* get luxels */
2395 luxel = SUPER_LUXEL( lightmapNum, x, y );
2396 deluxel = SUPER_DELUXEL( x, y );
2397
2398 /* store light direction */
2399 if( deluxemap && lightmapNum == 0 )
2400 VectorCopy( dirSample, deluxel );
2401
2402 /* store the sample back in super luxels */
2403 if( samples > 0.01f )
2404 {
2405 VectorScale( sample, (1.0f / samples), luxel );
2406 luxel[ 3 ] = 1.0f;
2407 }
2408
2409 /* if any samples were mapped in any way, store ambient color */
2410 else if( mappedSamples > 0 )
2411 {
2412 if( lightmapNum == 0 )
2413 VectorCopy( ambientColor, luxel );
2414 else
2415 VectorClear( luxel );
2416 luxel[ 3 ] = 1.0f;
2417 }
2418
2419 /* store a bogus value to be fixed later */
2420 else
2421 {
2422 VectorClear( luxel );
2423 luxel[ 3 ] = -1.0f;
2424 }
2425 }
2426 }
2427
2428 /* setup */
2429 lm->used = 0;
2430 ClearBounds( colorMins, colorMaxs );
2431
2432 /* clean up and store into bsp luxels */
2433 for( y = 0; y < lm->h; y++ )
2434 {
2435 for( x = 0; x < lm->w; x++ )
2436 {
2437 /* get luxels */
2438 luxel = SUPER_LUXEL( lightmapNum, x, y );
2439 deluxel = SUPER_DELUXEL( x, y );
2440
2441 /* copy light direction */
2442 if( deluxemap && lightmapNum == 0 )
2443 VectorCopy( deluxel, dirSample );
2444
2445 /* is this a valid sample? */
2446 if( luxel[ 3 ] > 0.0f )
2447 {
2448 VectorCopy( luxel, sample );
2449 samples = luxel[ 3 ];
2450 numUsed++;
2451 lm->used++;
2452
2453 /* fix negative samples */
2454 for( j = 0; j < 3; j++ )
2455 {
2456 if( sample[ j ] < 0.0f )
2457 sample[ j ] = 0.0f;
2458 }
2459 }
2460 else
2461 {
2462 /* nick an average value from the neighbors */
2463 VectorClear( sample );
2464 VectorClear( dirSample );
2465 samples = 0.0f;
2466
2467 /* fixme: why is this disabled?? */
2468 for( sy = (y - 1); sy <= (y + 1); sy++ )
2469 {
2470 if( sy < 0 || sy >= lm->h )
2471 continue;
2472
2473 for( sx = (x - 1); sx <= (x + 1); sx++ )
2474 {
2475 if( sx < 0 || sx >= lm->w || (sx == x && sy == y) )
2476 continue;
2477
2478 /* get neighbor's particulars */
2479 luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2480 if( luxel[ 3 ] < 0.0f )
2481 continue;
2482 VectorAdd( sample, luxel, sample );
2483 samples += luxel[ 3 ];
2484 }
2485 }
2486
2487 /* no samples? */
2488 if( samples == 0.0f )
2489 {
2490 VectorSet( sample, -1.0f, -1.0f, -1.0f );
2491 samples = 1.0f;
2492 }
2493 else
2494 {
2495 numUsed++;
2496 lm->used++;
2497
2498 /* fix negative samples */
2499 for( j = 0; j < 3; j++ )
2500 {
2501 if( sample[ j ] < 0.0f )
2502 sample[ j ] = 0.0f;
2503 }
2504 }
2505 }
2506
2507 /* scale the sample */
2508 VectorScale( sample, (1.0f / samples), sample );
2509
2510 /* store the sample in the radiosity luxels */
2511 if( bounce > 0 )
2512 {
2513 radLuxel = RAD_LUXEL( lightmapNum, x, y );
2514 VectorCopy( sample, radLuxel );
2515
2516 /* if only storing bounced light, early out here */
2517 if( bounceOnly && !bouncing )
2518 continue;
2519 }
2520
2521 /* store the sample in the bsp luxels */
2522 bspLuxel = BSP_LUXEL( lightmapNum, x, y );
2523 bspDeluxel = BSP_DELUXEL( x, y );
2524
2525 VectorAdd( bspLuxel, sample, bspLuxel );
2526 if( deluxemap && lightmapNum == 0 )
2527 VectorAdd( bspDeluxel, dirSample, bspDeluxel );
2528
2529 /* add color to bounds for solid checking */
2530 if( samples > 0.0f )
2531 AddPointToBounds( bspLuxel, colorMins, colorMaxs );
2532 }
2533 }
2534
2535 /* set solid color */
2536 lm->solid[ lightmapNum ] = qfalse;
2537 VectorAdd( colorMins, colorMaxs, lm->solidColor[ lightmapNum ] );
2538 VectorScale( lm->solidColor[ lightmapNum ], 0.5f, lm->solidColor[ lightmapNum ] );
2539
2540 /* nocollapse prevents solid lightmaps */
2541 if( noCollapse == qfalse )
2542 {
2543 /* check solid color */
2544 VectorSubtract( colorMaxs, colorMins, sample );
2545 if( (sample[ 0 ] <= SOLID_EPSILON && sample[ 1 ] <= SOLID_EPSILON && sample[ 2 ] <= SOLID_EPSILON) ||
2546 (lm->w <= 2 && lm->h <= 2) ) /* small lightmaps get forced to solid color */
2547 {
2548 /* set to solid */
2549 VectorCopy( colorMins, lm->solidColor[ lightmapNum ] );
2550 lm->solid[ lightmapNum ] = qtrue;
2551 numSolidLightmaps++;
2552 }
2553
2554 /* if all lightmaps aren't solid, then none of them are solid */
2555 if( lm->solid[ lightmapNum ] != lm->solid[ 0 ] )
2556 {
2557 for( y = 0; y < MAX_LIGHTMAPS; y++ )
2558 {
2559 if( lm->solid[ y ] )
2560 numSolidLightmaps--;
2561 lm->solid[ y ] = qfalse;
2562 }
2563 }
2564 }
2565
2566 /* wrap bsp luxels if necessary */
2567 if( lm->wrap[ 0 ] )
2568 {
2569 for( y = 0; y < lm->h; y++ )
2570 {
2571 bspLuxel = BSP_LUXEL( lightmapNum, 0, y );
2572 bspLuxel2 = BSP_LUXEL( lightmapNum, lm->w - 1, y );
2573 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2574 VectorScale( bspLuxel, 0.5f, bspLuxel );
2575 VectorCopy( bspLuxel, bspLuxel2 );
2576 if( deluxemap && lightmapNum == 0 )
2577 {
2578 bspDeluxel = BSP_DELUXEL( 0, y );
2579 bspDeluxel2 = BSP_DELUXEL( lm->w - 1, y );
2580 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2581 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2582 VectorCopy( bspDeluxel, bspDeluxel2 );
2583 }
2584 }
2585 }
2586 if( lm->wrap[ 1 ] )
2587 {
2588 for( x = 0; x < lm->w; x++ )
2589 {
2590 bspLuxel = BSP_LUXEL( lightmapNum, x, 0 );
2591 bspLuxel2 = BSP_LUXEL( lightmapNum, x, lm->h - 1 );
2592 VectorAdd( bspLuxel, bspLuxel2, bspLuxel );
2593 VectorScale( bspLuxel, 0.5f, bspLuxel );
2594 VectorCopy( bspLuxel, bspLuxel2 );
2595 if( deluxemap && lightmapNum == 0 )
2596 {
2597 bspDeluxel = BSP_DELUXEL( x, 0 );
2598 bspDeluxel2 = BSP_DELUXEL( x, lm->h - 1 );
2599 VectorAdd( bspDeluxel, bspDeluxel2, bspDeluxel );
2600 VectorScale( bspDeluxel, 0.5f, bspDeluxel );
2601 VectorCopy( bspDeluxel, bspDeluxel2 );
2602 }
2603 }
2604 }
2605 }
2606 }
2607
2608 /* -----------------------------------------------------------------
2609 collapse non-unique lightmaps
2610 ----------------------------------------------------------------- */
2611
2612 if( noCollapse == qfalse && deluxemap == qfalse )
2613 {
2614 /* note it */
2615 Sys_FPrintf( SYS_VRB, "collapsing..." );
2616
2617 /* set all twin refs to null */
2618 for( i = 0; i < numRawLightmaps; i++ )
2619 {
2620 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2621 {
2622 rawLightmaps[ i ].twins[ lightmapNum ] = NULL;
2623 rawLightmaps[ i ].twinNums[ lightmapNum ] = -1;
2624 rawLightmaps[ i ].numStyledTwins = 0;
2625 }
2626 }
2627
2628 /* walk the list of raw lightmaps */
2629 for( i = 0; i < numRawLightmaps; i++ )
2630 {
2631 /* get lightmap */
2632 lm = &rawLightmaps[ i ];
2633
2634 /* walk lightmaps */
2635 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2636 {
2637 /* early outs */
2638 if( lm->bspLuxels[ lightmapNum ] == NULL ||
2639 lm->twins[ lightmapNum ] != NULL )
2640 continue;
2641
2642 /* find all lightmaps that are virtually identical to this one */
2643 for( j = i + 1; j < numRawLightmaps; j++ )
2644 {
2645 /* get lightmap */
2646 lm2 = &rawLightmaps[ j ];
2647
2648 /* walk lightmaps */
2649 for( lightmapNum2 = 0; lightmapNum2 < MAX_LIGHTMAPS; lightmapNum2++ )
2650 {
2651 /* early outs */
2652 if( lm2->bspLuxels[ lightmapNum2 ] == NULL ||
2653 lm2->twins[ lightmapNum2 ] != NULL )
2654 continue;
2655
2656 /* compare them */
2657 if( CompareBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2658 {
2659 /* merge and set twin */
2660 if( MergeBSPLuxels( lm, lightmapNum, lm2, lightmapNum2 ) )
2661 {
2662 lm2->twins[ lightmapNum2 ] = lm;
2663 lm2->twinNums[ lightmapNum2 ] = lightmapNum;
2664 numTwins++;
2665 numTwinLuxels += (lm->w * lm->h);
2666
2667 /* count styled twins */
2668 if( lightmapNum > 0 )
2669 lm->numStyledTwins++;
2670 }
2671 }
2672 }
2673 }
2674 }
2675 }
2676 }
2677
2678 /* -----------------------------------------------------------------
2679 sort raw lightmaps by shader
2680 ----------------------------------------------------------------- */
2681
2682 /* note it */
2683 Sys_FPrintf( SYS_VRB, "sorting..." );
2684
2685 /* allocate a new sorted list */
2686 if( sortLightmaps == NULL )
2687 sortLightmaps = safe_malloc( numRawLightmaps * sizeof( int ) );
2688
2689 /* fill it out and sort it */
2690 for( i = 0; i < numRawLightmaps; i++ )
2691 sortLightmaps[ i ] = i;
2692 qsort( sortLightmaps, numRawLightmaps, sizeof( int ), CompareRawLightmap );
2693
2694 /* -----------------------------------------------------------------
2695 allocate output lightmaps
2696 ----------------------------------------------------------------- */
2697
2698 /* note it */
2699 Sys_FPrintf( SYS_VRB, "allocating..." );
2700
2701 /* kill all existing output lightmaps */
2702 if( outLightmaps != NULL )
2703 {
2704 for( i = 0; i < numOutLightmaps; i++ )
2705 {
2706 free( outLightmaps[ i ].lightBits );
2707 free( outLightmaps[ i ].bspLightBytes );
2708 }
2709 free( outLightmaps );
2710 outLightmaps = NULL;
2711 }
2712
2713 numLightmapShaders = 0;
2714 numOutLightmaps = 0;
2715 numBSPLightmaps = 0;
2716 numExtLightmaps = 0;
2717
2718 /* find output lightmap */
2719 for( i = 0; i < numRawLightmaps; i++ )
2720 {
2721 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2722 FindOutLightmaps( lm );
2723 }
2724
2725 /* set output numbers in twinned lightmaps */
2726 for( i = 0; i < numRawLightmaps; i++ )
2727 {
2728 /* get lightmap */
2729 lm = &rawLightmaps[ sortLightmaps[ i ] ];
2730
2731 /* walk lightmaps */
2732 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2733 {
2734 /* get twin */
2735 lm2 = lm->twins[ lightmapNum ];
2736 if( lm2 == NULL )
2737 continue;
2738 lightmapNum2 = lm->twinNums[ lightmapNum ];
2739
2740 /* find output lightmap from twin */
2741 lm->outLightmapNums[ lightmapNum ] = lm2->outLightmapNums[ lightmapNum2 ];
2742 lm->lightmapX[ lightmapNum ] = lm2->lightmapX[ lightmapNum2 ];
2743 lm->lightmapY[ lightmapNum ] = lm2->lightmapY[ lightmapNum2 ];
2744 }
2745 }
2746
2747 /* -----------------------------------------------------------------
2748 store output lightmaps
2749 ----------------------------------------------------------------- */
2750
2751 /* note it */
2752 Sys_FPrintf( SYS_VRB, "storing..." );
2753
2754 /* count the bsp lightmaps and allocate space */
2755 if( bspLightBytes != NULL )
2756 free( bspLightBytes );
2757 if( numBSPLightmaps == 0 || externalLightmaps )
2758 {
2759 numBSPLightBytes = 0;
2760 bspLightBytes = NULL;
2761 }
2762 else
2763 {
2764 numBSPLightBytes = (numBSPLightmaps * game->lightmapSize * game->lightmapSize * 3);
2765 bspLightBytes = safe_malloc( numBSPLightBytes );
2766 memset( bspLightBytes, 0, numBSPLightBytes );
2767 }
2768
2769 /* walk the list of output lightmaps */
2770 for( i = 0; i < numOutLightmaps; i++ )
2771 {
2772 /* get output lightmap */
2773 olm = &outLightmaps[ i ];
2774
2775 /* is this a valid bsp lightmap? */
2776 if( olm->lightmapNum >= 0 && !externalLightmaps )
2777 {
2778 /* copy lighting data */
2779 lb = bspLightBytes + (olm->lightmapNum * game->lightmapSize * game->lightmapSize * 3);
2780 memcpy( lb, olm->bspLightBytes, game->lightmapSize * game->lightmapSize * 3 );
2781
2782 /* copy direction data */
2783 if( deluxemap )
2784 {
2785 lb = bspLightBytes + ((olm->lightmapNum + 1) * game->lightmapSize * game->lightmapSize * 3);
2786 memcpy( lb, olm->bspDirBytes, game->lightmapSize * game->lightmapSize * 3 );
2787 }
2788 }
2789
2790 /* external lightmap? */
2791 if( olm->lightmapNum < 0 || olm->extLightmapNum >= 0 || externalLightmaps )
2792 {
2793 /* make a directory for the lightmaps */
2794 Q_mkdir( dirname );
2795
2796 /* set external lightmap number */
2797 olm->extLightmapNum = numExtLightmaps;
2798
2799 /* write lightmap */
2800 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2801 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2802 WriteTGA24( filename, olm->bspLightBytes, olm->customWidth, olm->customHeight, qtrue );
2803 numExtLightmaps++;
2804
2805 /* write deluxemap */
2806 if( deluxemap )
2807 {
2808 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, numExtLightmaps );
2809 Sys_FPrintf( SYS_VRB, "\nwriting %s", filename );
2810 WriteTGA24( filename, olm->bspDirBytes, olm->customWidth, olm->customHeight, qtrue );
2811 numExtLightmaps++;
2812
2813 if( debugDeluxemap )
2814 olm->extLightmapNum++;
2815 }
2816 }
2817 }
2818
2819 if( numExtLightmaps > 0 )
2820 Sys_FPrintf( SYS_VRB, "\n" );
2821
2822 /* delete unused external lightmaps */
2823 for( i = numExtLightmaps; i; i++ )
2824 {
2825 /* determine if file exists */
2826 sprintf( filename, "%s/" EXTERNAL_LIGHTMAP, dirname, i );
2827 if( !FileExists( filename ) )
2828 break;
2829
2830 /* delete it */
2831 remove( filename );
2832 }
2833
2834 /* -----------------------------------------------------------------
2835 project the lightmaps onto the bsp surfaces
2836 ----------------------------------------------------------------- */
2837
2838 /* note it */
2839 Sys_FPrintf( SYS_VRB, "projecting..." );
2840
2841 /* walk the list of surfaces */
2842 for( i = 0; i < numBSPDrawSurfaces; i++ )
2843 {
2844 /* get the surface and info */
2845 ds = &bspDrawSurfaces[ i ];
2846 info = &surfaceInfos[ i ];
2847 lm = info->lm;
2848 olm = NULL;
2849
2850 /* handle surfaces with identical parent */
2851 if( info->parentSurfaceNum >= 0 )
2852 {
2853 /* preserve original data and get parent */
2854 parent = &bspDrawSurfaces[ info->parentSurfaceNum ];
2855 memcpy( &dsTemp, ds, sizeof( *ds ) );
2856
2857 /* overwrite child with parent data */
2858 memcpy( ds, parent, sizeof( *ds ) );
2859
2860 /* restore key parts */
2861 ds->fogNum = dsTemp.fogNum;
2862 ds->firstVert = dsTemp.firstVert;
2863 ds->firstIndex = dsTemp.firstIndex;
2864 memcpy( ds->lightmapVecs, dsTemp.lightmapVecs, sizeof( dsTemp.lightmapVecs ) );
2865
2866 /* set vertex data */
2867 dv = &bspDrawVerts[ ds->firstVert ];
2868 dvParent = &bspDrawVerts[ parent->firstVert ];
2869 for( j = 0; j < ds->numVerts; j++ )
2870 {
2871 memcpy( dv[ j ].lightmap, dvParent[ j ].lightmap, sizeof( dv[ j ].lightmap ) );
2872 memcpy( dv[ j ].color, dvParent[ j ].color, sizeof( dv[ j ].color ) );
2873 }
2874
2875 /* skip the rest */
2876 continue;
2877 }
2878
2879 /* handle vertex lit or approximated surfaces */
2880 else if( lm == NULL || lm->outLightmapNums[ 0 ] < 0 )
2881 {
2882 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2883 {
2884 ds->lightmapNum[ lightmapNum ] = -3;
2885 ds->lightmapStyles[ lightmapNum ] = ds->vertexStyles[ lightmapNum ];
2886 }
2887 }
2888
2889 /* handle lightmapped surfaces */
2890 else
2891 {
2892 /* walk lightmaps */
2893 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2894 {
2895 /* set style */
2896 ds->lightmapStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2897
2898 /* handle unused style */
2899 if( lm->styles[ lightmapNum ] == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
2900 {
2901 ds->lightmapNum[ lightmapNum ] = -3;
2902 continue;
2903 }
2904
2905 /* get output lightmap */
2906 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
2907
2908 /* set bsp lightmap number */
2909 ds->lightmapNum[ lightmapNum ] = olm->lightmapNum;
2910
2911 /* deluxemap debugging makes the deluxemap visible */
2912 if( deluxemap && debugDeluxemap && lightmapNum == 0 )
2913 ds->lightmapNum[ lightmapNum ]++;
2914
2915 /* calc lightmap origin in texture space */
2916 lmx = (float) lm->lightmapX[ lightmapNum ] / (float) olm->customWidth;
2917 lmy = (float) lm->lightmapY[ lightmapNum ] / (float) olm->customHeight;
2918
2919 /* calc lightmap st coords */
2920 dv = &bspDrawVerts[ ds->firstVert ];
2921 ydv = &yDrawVerts[ ds->firstVert ];
2922 for( j = 0; j < ds->numVerts; j++ )
2923 {
2924 if( lm->solid[ lightmapNum ] )
2925 {
2926 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (0.5f / (float) olm->customWidth);
2927 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (0.5f / (float) olm->customWidth);
2928 }
2929 else
2930 {
2931 dv[ j ].lightmap[ lightmapNum ][ 0 ] = lmx + (ydv[ j ].lightmap[ 0 ][ 0 ] / (superSample * olm->customWidth));
2932 dv[ j ].lightmap[ lightmapNum ][ 1 ] = lmy + (ydv[ j ].lightmap[ 0 ][ 1 ] / (superSample * olm->customHeight));
2933 }
2934 }
2935 }
2936 }
2937
2938 /* store vertex colors */
2939 dv = &bspDrawVerts[ ds->firstVert ];
2940 for( j = 0; j < ds->numVerts; j++ )
2941 {
2942 /* walk lightmaps */
2943 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2944 {
2945 /* handle unused style */
2946 if( ds->vertexStyles[ lightmapNum ] == LS_NONE )
2947 VectorClear( color );
2948 else
2949 {
2950 /* get vertex color */
2951 luxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + j );
2952 VectorCopy( luxel, color );
2953
2954 /* set minimum light */
2955 if( lightmapNum == 0 )
2956 {
2957 for( k = 0; k < 3; k++ )
2958 if( color[ k ] < minVertexLight[ k ] )
2959 color[ k ] = minVertexLight[ k ];
2960 }
2961 }
2962
2963 /* store to bytes */
2964 if( !info->si->noVertexLight )
2965 ColorToBytes( color, dv[ j ].color[ lightmapNum ], info->si->vertexScale );
2966 }
2967 }
2968
2969 /* surfaces with styled lightmaps and a style marker get a custom generated shader (fixme: make this work with external lightmaps) */
2970 if( olm != NULL && lm != NULL && lm->styles[ 1 ] != LS_NONE && game->load != LoadRBSPFile ) //% info->si->styleMarker > 0 )
2971 {
2972 qboolean dfEqual;
2973 char key[ 32 ], styleStage[ 512 ], styleStages[ 4096 ], rgbGen[ 128 ], alphaGen[ 128 ];
2974
2975
2976 /* setup */
2977 sprintf( styleStages, "\n\t// Q3Map2 custom lightstyle stage(s)\n" );
2978 dv = &bspDrawVerts[ ds->firstVert ];
2979
2980 /* depthFunc equal? */
2981 if( info->si->styleMarker == 2 || info->si->implicitMap == IM_MASKED )
2982 dfEqual = qtrue;
2983 else
2984 dfEqual = qfalse;
2985
2986 /* generate stages for styled lightmaps */
2987 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2988 {
2989 /* early out */
2990 style = lm->styles[ lightmapNum ];
2991 if( style == LS_NONE || lm->outLightmapNums[ lightmapNum ] < 0 )
2992 continue;
2993
2994 /* get output lightmap */
2995 olm = &outLightmaps[ lm->outLightmapNums[ lightmapNum ] ];
2996
2997 /* lightmap name */
2998 if( lm->outLightmapNums[ lightmapNum ] == lm->outLightmapNums[ 0 ] )
2999 strcpy( lightmapName, "$lightmap" );
3000 else
3001 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3002
3003 /* get rgbgen string */
3004 if( rgbGenValues[ style ] == NULL )
3005 {
3006 sprintf( key, "_style%drgbgen", style );
3007 rgbGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3008 if( rgbGenValues[ style ][ 0 ] == '\0' )
3009 rgbGenValues[ style ] = "wave noise 0.5 1 0 5.37";
3010 }
3011 rgbGen[ 0 ] = '\0';
3012 if( rgbGenValues[ style ][ 0 ] != '\0' )
3013 sprintf( rgbGen, "\t\trgbGen %s // style %d\n", rgbGenValues[ style ], style );
3014 else
3015 rgbGen[ 0 ] = '\0';
3016
3017 /* get alphagen string */
3018 if( alphaGenValues[ style ] == NULL )
3019 {
3020 sprintf( key, "_style%dalphagen", style );
3021 alphaGenValues[ style ] = (char*) ValueForKey( &entities[ 0 ], key );
3022 }
3023 if( alphaGenValues[ style ][ 0 ] != '\0' )
3024 sprintf( alphaGen, "\t\talphaGen %s // style %d\n", alphaGenValues[ style ], style );
3025 else
3026 alphaGen[ 0 ] = '\0';
3027
3028 /* calculate st offset */
3029 lmx = dv[ 0 ].lightmap[ lightmapNum ][ 0 ] - dv[ 0 ].lightmap[ 0 ][ 0 ];
3030 lmy = dv[ 0 ].lightmap[ lightmapNum ][ 1 ] - dv[ 0 ].lightmap[ 0 ][ 1 ];
3031
3032 /* create additional stage */
3033 if( lmx == 0.0f && lmy == 0.0f )
3034 {
3035 sprintf( styleStage, "\t{\n"
3036 "\t\tmap %s\n" /* lightmap */
3037 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3038 "%s" /* depthFunc equal */
3039 "%s" /* rgbGen */
3040 "%s" /* alphaGen */
3041 "\t\ttcGen lightmap\n"
3042 "\t}\n",
3043 lightmapName,
3044 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
3045 rgbGen,
3046 alphaGen );
3047 }
3048 else
3049 {
3050 sprintf( styleStage, "\t{\n"
3051 "\t\tmap %s\n" /* lightmap */
3052 "\t\tblendFunc GL_SRC_ALPHA GL_ONE\n"
3053 "%s" /* depthFunc equal */
3054 "%s" /* rgbGen */
3055 "%s" /* alphaGen */
3056 "\t\ttcGen lightmap\n"
3057 "\t\ttcMod transform 1 0 0 1 %1.5f %1.5f\n" /* st offset */
3058 "\t}\n",
3059 lightmapName,
3060 (dfEqual ? "\t\tdepthFunc equal\n" : ""),
3061 rgbGen,
3062 alphaGen,
3063 lmx, lmy );
3064
3065 }
3066
3067 /* concatenate */
3068 strcat( styleStages, styleStage );
3069 }
3070
3071 /* create custom shader */
3072 if( info->si->styleMarker == 2 )
3073 csi = CustomShader( info->si, "q3map_styleMarker2", styleStages );
3074 else
3075 csi = CustomShader( info->si, "q3map_styleMarker", styleStages );
3076
3077 /* emit remap command */
3078 //% EmitVertexRemapShader( csi->shader, info->si->shader );
3079
3080 /* store it */
3081 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3082 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3083 //% Sys_Printf( ")\n" );
3084 }
3085
3086 /* devise a custom shader for this surface (fixme: make this work with light styles) */
3087 else if( olm != NULL && lm != NULL && !externalLightmaps &&
3088 (olm->customWidth != game->lightmapSize || olm->customHeight != game->lightmapSize) )
3089 {
3090 /* get output lightmap */
3091 olm = &outLightmaps[ lm->outLightmapNums[ 0 ] ];
3092
3093 /* do some name mangling */
3094 sprintf( lightmapName, "maps/%s/" EXTERNAL_LIGHTMAP, mapName, olm->extLightmapNum );
3095
3096 /* create custom shader */
3097 csi = CustomShader( info->si, "$lightmap", lightmapName );
3098
3099 /* store it */
3100 //% Sys_Printf( "Emitting: %s (%d", csi->shader, strlen( csi->shader ) );
3101 ds->shaderNum = EmitShader( csi->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3102 //% Sys_Printf( ")\n" );
3103 }
3104
3105 /* use the normal plain-jane shader */
3106 else
3107 ds->shaderNum = EmitShader( info->si->shader, &bspShaders[ ds->shaderNum ].contentFlags, &bspShaders[ ds->shaderNum ].surfaceFlags );
3108 }
3109
3110 /* finish */
3111 Sys_FPrintf( SYS_VRB, "done.\n" );
3112
3113 /* calc num stored */
3114 numStored = numBSPLightBytes / 3;
3115 efficiency = (numStored <= 0)
3116 ? 0
3117 : (float) numUsed / (float) numStored;
3118
3119 /* print stats */
3120 Sys_Printf( "%9d luxels used\n", numUsed );
3121 Sys_Printf( "%9d luxels stored (%3.2f percent efficiency)\n", numStored, efficiency * 100.0f );
3122 Sys_Printf( "%9d solid surface lightmaps\n", numSolidLightmaps );
3123 Sys_Printf( "%9d identical surface lightmaps, using %d luxels\n", numTwins, numTwinLuxels );
3124 Sys_Printf( "%9d vertex forced surfaces\n", numSurfsVertexForced );
3125 Sys_Printf( "%9d vertex approximated surfaces\n", numSurfsVertexApproximated );
3126 Sys_Printf( "%9d BSP lightmaps\n", numBSPLightmaps );
3127 Sys_Printf( "%9d total lightmaps\n", numOutLightmaps );
3128 Sys_Printf( "%9d unique lightmap/shader combinations\n", numLightmapShaders );
3129
3130 /* write map shader file */
3131 WriteMapShaderFile();
3132 }
3133
3134
3135
3136
3137