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