]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/light_ydnar.c
force subsampling only where REALLY needed
[xonotic/netradiant.git] / tools / quake3 / q3map2 / light_ydnar.c
1 /* -------------------------------------------------------------------------------
2
3 Copyright (C) 1999-2007 id Software, Inc. and contributors.
4 For a list of contributors, see the accompanying CONTRIBUTORS file.
5
6 This file is part of GtkRadiant.
7
8 GtkRadiant is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 GtkRadiant is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GtkRadiant; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21
22 ----------------------------------------------------------------------------------
23
24 This code has been altered significantly from its original form, to support
25 several games based on the Quake III Arena engine, in the form of "Q3Map2."
26
27 ------------------------------------------------------------------------------- */
28
29
30
31 /* marker */
32 #define LIGHT_YDNAR_C
33
34
35
36 /* dependencies */
37 #include "q3map2.h"
38
39
40
41
42 /*
43 ColorToBytes()
44 ydnar: moved to here 2001-02-04
45 */
46
47 void ColorToBytes( const float *color, byte *colorBytes, float scale )
48 {
49         int             i;
50         float   max, gamma;
51         vec3_t  sample;
52         float   inv, dif;
53         
54         
55         /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
56         if( scale <= 0.0f )
57                 scale = 1.0f;
58         
59         /* make a local copy */
60         VectorScale( color, scale, sample );
61         
62         /* muck with it */
63         gamma = 1.0f / lightmapGamma;
64         for( i = 0; i < 3; i++ )
65         {
66                 /* handle negative light */
67                 if( sample[ i ] < 0.0f )
68                 {
69                         sample[ i ] = 0.0f;
70                         continue;
71                 }
72                 
73                 /* gamma */
74                 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
75         }
76
77         if (lightmapExposure == 1)
78         {
79                 /* clamp with color normalization */
80                 max = sample[ 0 ];
81                 if( sample[ 1 ] > max )
82                         max = sample[ 1 ];
83                 if( sample[ 2 ] > max )
84                         max = sample[ 2 ];
85                 if( max > 255.0f )
86                         VectorScale( sample, (255.0f / max), sample );
87         }
88         else
89         {
90                 if (lightmapExposure==0)
91                 {
92                         lightmapExposure=1.0f;
93                 }
94                 inv=1.f/lightmapExposure;
95                 //Exposure
96
97                 max = sample[ 0 ];
98                 if( sample[ 1 ] > max )
99                         max = sample[ 1 ];
100                 if( sample[ 2 ] > max )
101                         max = sample[ 2 ];
102
103                 dif = (1-  exp(-max * inv) )  *  255;
104
105                 if (max >0)
106                 {
107                         dif = dif / max;
108                 }
109                 else
110                 {
111                         dif = 0;
112                 }
113
114                 for (i=0;i<3;i++)
115                 {
116                         sample[i]*=dif;
117                 }
118         }
119
120         
121         /* compensate for ingame overbrighting/bitshifting */
122         VectorScale( sample, (1.0f / lightmapCompensate), sample );
123         
124         /* store it off */
125         colorBytes[ 0 ] = sample[ 0 ];
126         colorBytes[ 1 ] = sample[ 1 ];
127         colorBytes[ 2 ] = sample[ 2 ];
128 }
129
130
131
132 /* -------------------------------------------------------------------------------
133
134 this section deals with phong shading (normal interpolation across brush faces)
135
136 ------------------------------------------------------------------------------- */
137
138 /*
139 SmoothNormals()
140 smooths together coincident vertex normals across the bsp
141 */
142
143 #define MAX_SAMPLES                             256
144 #define THETA_EPSILON                   0.000001
145 #define EQUAL_NORMAL_EPSILON    0.01
146
147 void SmoothNormals( void )
148 {
149         int                                     i, j, k, f, cs, numVerts, numVotes, fOld, start;
150         float                           shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
151         bspDrawSurface_t        *ds;
152         shaderInfo_t            *si;
153         float                           *shadeAngles;
154         byte                            *smoothed;
155         vec3_t                          average, diff;
156         int                                     indexes[ MAX_SAMPLES ];
157         vec3_t                          votes[ MAX_SAMPLES ];
158         
159         
160         /* allocate shade angle table */
161         shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
162         memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
163         
164         /* allocate smoothed table */
165         cs = (numBSPDrawVerts / 8) + 1;
166         smoothed = safe_malloc( cs );
167         memset( smoothed, 0, cs );
168         
169         /* set default shade angle */
170         defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
171         maxShadeAngle = 0;
172         
173         /* run through every surface and flag verts belonging to non-lightmapped surfaces
174            and set per-vertex smoothing angle */
175         for( i = 0; i < numBSPDrawSurfaces; i++ )
176         {
177                 /* get drawsurf */
178                 ds = &bspDrawSurfaces[ i ];
179                 
180                 /* get shader for shade angle */
181                 si = surfaceInfos[ i ].si;
182                 if( si->shadeAngleDegrees )
183                         shadeAngle = DEG2RAD( si->shadeAngleDegrees );
184                 else
185                         shadeAngle = defaultShadeAngle;
186                 if( shadeAngle > maxShadeAngle )
187                         maxShadeAngle = shadeAngle;
188                 
189                 /* flag its verts */
190                 for( j = 0; j < ds->numVerts; j++ )
191                 {
192                         f = ds->firstVert + j;
193                         shadeAngles[ f ] = shadeAngle;
194                         if( ds->surfaceType == MST_TRIANGLE_SOUP )
195                                 smoothed[ f >> 3 ] |= (1 << (f & 7));
196                 }
197                 
198                 /* ydnar: optional force-to-trisoup */
199                 if( trisoup && ds->surfaceType == MST_PLANAR )
200                 {
201                         ds->surfaceType = MST_TRIANGLE_SOUP;
202                         ds->lightmapNum[ 0 ] = -3;
203                 }
204         }
205         
206         /* bail if no surfaces have a shade angle */
207         if( maxShadeAngle == 0 )
208         {
209                 free( shadeAngles );
210                 free( smoothed );
211                 return;
212         }
213         
214         /* init pacifier */
215         fOld = -1;
216         start = I_FloatTime();
217         
218         /* go through the list of vertexes */
219         for( i = 0; i < numBSPDrawVerts; i++ )
220         {
221                 /* print pacifier */
222                 f = 10 * i / numBSPDrawVerts;
223                 if( f != fOld )
224                 {
225                         fOld = f;
226                         Sys_Printf( "%i...", f );
227                 }
228                 
229                 /* already smoothed? */
230                 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )
231                         continue;
232                 
233                 /* clear */
234                 VectorClear( average );
235                 numVerts = 0;
236                 numVotes = 0;
237                 
238                 /* build a table of coincident vertexes */
239                 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
240                 {
241                         /* already smoothed? */
242                         if( smoothed[ j >> 3 ] & (1 << (j & 7)) )
243                                 continue;
244                         
245                         /* test vertexes */
246                         if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )
247                                 continue;
248                         
249                         /* use smallest shade angle */
250                         shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);
251                         
252                         /* check shade angle */
253                         dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
254                         if( dot > 1.0 )
255                                 dot = 1.0;
256                         else if( dot < -1.0 )
257                                 dot = -1.0;
258                         testAngle = acos( dot ) + THETA_EPSILON;
259                         if( testAngle >= shadeAngle )
260                         {
261                                 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
262                                 continue;
263                         }
264                         //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
265                         
266                         /* add to the list */
267                         indexes[ numVerts++ ] = j;
268                         
269                         /* flag vertex */
270                         smoothed[ j >> 3 ] |= (1 << (j & 7));
271                         
272                         /* see if this normal has already been voted */
273                         for( k = 0; k < numVotes; k++ )
274                         {
275                                 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
276                                 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
277                                         fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
278                                         fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )
279                                         break;
280                         }
281                         
282                         /* add a new vote? */
283                         if( k == numVotes && numVotes < MAX_SAMPLES )
284                         {
285                                 VectorAdd( average, bspDrawVerts[ j ].normal, average );
286                                 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
287                                 numVotes++;
288                         }
289                 }
290                 
291                 /* don't average for less than 2 verts */
292                 if( numVerts < 2 )
293                         continue;
294                 
295                 /* average normal */
296                 if( VectorNormalize( average, average ) > 0 )
297                 {
298                         /* smooth */
299                         for( j = 0; j < numVerts; j++ )
300                                 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
301                 }
302         }
303         
304         /* free the tables */
305         free( shadeAngles );
306         free( smoothed );
307         
308         /* print time */
309         Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );
310 }
311
312
313
314 /* -------------------------------------------------------------------------------
315
316 this section deals with phong shaded lightmap tracing
317
318 ------------------------------------------------------------------------------- */
319
320 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
321
322 /*
323 CalcTangentVectors()
324 calculates the st tangent vectors for normalmapping
325 */
326
327 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )
328 {
329         int                     i;
330         float           bb, s, t;
331         vec3_t          bary;
332         
333         
334         /* calculate barycentric basis for the triangle */
335         bb = (dv[ 1 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 2 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]) - (dv[ 2 ]->st[ 0 ] - dv[ 0 ]->st[ 0 ]) * (dv[ 1 ]->st[ 1 ] - dv[ 0 ]->st[ 1 ]);
336         if( fabs( bb ) < 0.00000001f )
337                 return qfalse;
338         
339         /* do each vertex */
340         for( i = 0; i < numVerts; i++ )
341         {
342                 /* calculate s tangent vector */
343                 s = dv[ i ]->st[ 0 ] + 10.0f;
344                 t = dv[ i ]->st[ 1 ];
345                 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
346                 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
347                 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
348                 
349                 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
350                 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
351                 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
352                 
353                 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
354                 VectorNormalize( stv[ i ], stv[ i ] );
355                 
356                 /* calculate t tangent vector */
357                 s = dv[ i ]->st[ 0 ];
358                 t = dv[ i ]->st[ 1 ] + 10.0f;
359                 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;
360                 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;
361                 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;
362                 
363                 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
364                 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
365                 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
366                 
367                 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
368                 VectorNormalize( ttv[ i ], ttv[ i ] );
369                 
370                 /* debug code */
371                 //%     Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
372                 //%             stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
373         }
374         
375         /* return to caller */
376         return qtrue;
377 }
378
379
380
381
382 /*
383 PerturbNormal()
384 perterbs the normal by the shader's normalmap in tangent space
385 */
386
387 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )
388 {
389         int                     i;
390         vec4_t          bump;
391         
392         
393         /* passthrough */
394         VectorCopy( dv->normal, pNormal );
395         
396         /* sample normalmap */
397         if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )
398                 return;
399         
400         /* remap sampled normal from [0,255] to [-1,-1] */
401         for( i = 0; i < 3; i++ )
402                 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);
403         
404         /* scale tangent vectors and add to original normal */
405         VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
406         VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
407         VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
408         
409         /* renormalize and return */
410         VectorNormalize( pNormal, pNormal );
411 }
412
413
414
415 /*
416 MapSingleLuxel()
417 maps a luxel for triangle bv at
418 */
419
420 #define NUDGE                   0.5f
421 #define BOGUS_NUDGE             -99999.0f
422
423 static int MapSingleLuxel( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv, vec4_t plane, float pass, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
424 {
425         int                             i, x, y, numClusters, *clusters, pointCluster, *cluster;
426         float                   *luxel, *origin, *normal, d, lightmapSampleOffset;
427         shaderInfo_t    *si;
428         vec3_t                  pNormal;
429         vec3_t                  vecs[ 3 ];
430         vec3_t                  nudged;
431         vec3_t                  cverts[ 3 ];
432         vec3_t                  temp;
433         vec4_t                  sideplane, hostplane;
434         vec3_t                  origintwo;
435         int                             j, next;
436         float                   e;
437         float                   *nudge;
438         static float    nudges[][ 2 ] =
439                                         {
440                                                 //%{ 0, 0 },            /* try center first */
441                                                 { -NUDGE, 0 },          /* left */
442                                                 { NUDGE, 0 },           /* right */
443                                                 { 0, NUDGE },           /* up */
444                                                 { 0, -NUDGE },          /* down */
445                                                 { -NUDGE, NUDGE },      /* left/up */
446                                                 { NUDGE, -NUDGE },      /* right/down */
447                                                 { NUDGE, NUDGE },       /* right/up */
448                                                 { -NUDGE, -NUDGE },     /* left/down */
449                                                 { BOGUS_NUDGE, BOGUS_NUDGE }
450                                         };
451         
452         
453         /* find luxel xy coords (fixme: subtract 0.5?) */
454         x = dv->lightmap[ 0 ][ 0 ];
455         y = dv->lightmap[ 0 ][ 1 ];
456         if( x < 0 )
457                 x = 0;
458         else if( x >= lm->sw )
459                 x = lm->sw - 1;
460         if( y < 0 )
461                 y = 0;
462         else if( y >= lm->sh )
463                 y = lm->sh - 1;
464         
465         /* set shader and cluster list */
466         if( info != NULL )
467         {
468                 si = info->si;
469                 numClusters = info->numSurfaceClusters;
470                 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
471         }
472         else
473         {
474                 si = NULL;
475                 numClusters = 0;
476                 clusters = NULL;
477         }
478         
479         /* get luxel, origin, cluster, and normal */
480         luxel = SUPER_LUXEL( 0, x, y );
481         origin = SUPER_ORIGIN( x, y );
482         normal = SUPER_NORMAL( x, y );
483         cluster = SUPER_CLUSTER( x, y );
484         
485         /* don't attempt to remap occluded luxels for planar surfaces */
486         if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )
487                 return (*cluster);
488         
489         /* only average the normal for premapped luxels */
490         else if( (*cluster) >= 0 )
491         {
492                 /* do bumpmap calculations */
493                 if( stv != NULL )
494                         PerturbNormal( dv, si, pNormal, stv, ttv );
495                 else
496                         VectorCopy( dv->normal, pNormal );
497                 
498                 /* add the additional normal data */
499                 VectorAdd( normal, pNormal, normal );
500                 luxel[ 3 ] += 1.0f;
501                 return (*cluster);
502         }
503         
504         /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
505         
506         /* get origin */
507         
508         /* axial lightmap projection */
509         if( lm->vecs != NULL )
510         {
511                 /* calculate an origin for the sample from the lightmap vectors */
512                 VectorCopy( lm->origin, origin );
513                 for( i = 0; i < 3; i++ )
514                 {
515                         /* add unless it's the axis, which is taken care of later */
516                         if( i == lm->axisNum )
517                                 continue;
518                         origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);
519                 }
520                 
521                 /* project the origin onto the plane */
522                 d = DotProduct( origin, plane ) - plane[ 3 ];
523                 d /= plane[ lm->axisNum ];
524                 origin[ lm->axisNum ] -= d;
525         }
526         
527         /* non axial lightmap projection (explicit xyz) */
528         else
529                 VectorCopy( dv->xyz, origin );
530
531         //////////////////////
532         //27's test to make sure samples stay within the triangle boundaries
533         //1) Test the sample origin to see if it lays on the wrong side of any edge (x/y)
534         //2) if it does, nudge it onto the correct side.
535
536         if (worldverts!=NULL && lightmapTriangleCheck)
537         {
538                 for (j=0;j<3;j++)
539                 {
540                         VectorCopy(worldverts[j],cverts[j]);
541                 }
542                 PlaneFromPoints(hostplane,cverts[0],cverts[1],cverts[2]);
543
544                 for (j=0;j<3;j++)
545                 {
546                         for (i=0;i<3;i++)
547                         {
548                                 //build plane using 2 edges and a normal
549                                 next=(i+1)%3;
550
551                                 VectorCopy(cverts[next],temp);
552                                 VectorAdd(temp,hostplane,temp);
553                                 PlaneFromPoints(sideplane,cverts[i],cverts[ next ], temp);
554
555                                 //planetest sample point
556                                 e=DotProduct(origin,sideplane);
557                                 e=e-sideplane[3];
558                                 if (e>0)
559                                 {
560                                         //we're bad.
561                                         //VectorClear(origin);
562                                         //Move the sample point back inside triangle bounds
563                                         origin[0]-=sideplane[0]*(e+1);
564                                         origin[1]-=sideplane[1]*(e+1);
565                                         origin[2]-=sideplane[2]*(e+1);
566 #ifdef DEBUG_27_1
567                                         VectorClear(origin);
568 #endif
569                                 }
570                         }
571                 }
572         }
573
574         ////////////////////////
575         
576         /* planar surfaces have precalculated lightmap vectors for nudging */
577         if( lm->plane != NULL )
578         {
579                 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
580                 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
581                 VectorCopy( lm->plane, vecs[ 2 ] );
582         }
583         
584         /* non-planar surfaces must calculate them */
585         else
586         {
587                 if( plane != NULL )
588                         VectorCopy( plane, vecs[ 2 ] );
589                 else
590                         VectorCopy( dv->normal, vecs[ 2 ] );
591                 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
592         }
593         
594         /* push the origin off the surface a bit */
595         if( si != NULL )
596                 lightmapSampleOffset = si->lightmapSampleOffset;
597         else
598                 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
599         if( lm->axisNum < 0 )
600                 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
601         else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )
602                 origin[ lm->axisNum ] -= lightmapSampleOffset;
603         else
604                 origin[ lm->axisNum ] += lightmapSampleOffset;
605         
606         VectorCopy(origin,origintwo);
607         if(lightmapExtraVisClusterNudge)
608         {
609                 origintwo[0]+=vecs[2][0];
610                 origintwo[1]+=vecs[2][1];
611                 origintwo[2]+=vecs[2][2];
612         }
613
614         /* get cluster */
615         pointCluster = ClusterForPointExtFilter( origintwo, LUXEL_EPSILON, numClusters, clusters );
616         
617         /* another retarded hack, storing nudge count in luxel[ 1 ] */
618         luxel[ 1 ] = 0.0f;      
619         
620         /* point in solid? (except in dark mode) */
621         if( pointCluster < 0 && dark == qfalse )
622         {
623                 /* nudge the the location around */
624                 nudge = nudges[ 0 ];
625                 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
626                 {
627                         /* nudge the vector around a bit */
628                         for( i = 0; i < 3; i++ )
629                         {
630                                 /* set nudged point*/
631                                 nudged[ i ] = origintwo[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);
632                         }
633                         nudge += 2;
634                         
635                         /* get pvs cluster */
636                         pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
637                         if( pointCluster >= 0 )
638                                 VectorCopy( nudged, origin );
639                         luxel[ 1 ] += 1.0f;
640                 }
641         }
642         
643         /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
644         if( pointCluster < 0 && si != NULL && dark == qfalse )
645         {
646                 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
647                 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
648                 if( pointCluster >= 0 )
649                         VectorCopy( nudged, origin );
650                 luxel[ 1 ] += 1.0f;
651         }
652         
653         /* valid? */
654         if( pointCluster < 0 )
655         {
656                 (*cluster) = CLUSTER_OCCLUDED;
657                 VectorClear( origin );
658                 VectorClear( normal );
659                 numLuxelsOccluded++;
660                 return (*cluster);
661         }
662         
663         /* debug code */
664         //%     Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
665         
666         /* do bumpmap calculations */
667         if( stv )
668                 PerturbNormal( dv, si, pNormal, stv, ttv );
669         else
670                 VectorCopy( dv->normal, pNormal );
671         
672         /* store the cluster and normal */
673         (*cluster) = pointCluster;
674         VectorCopy( pNormal, normal );
675         
676         /* store explicit mapping pass and implicit mapping pass */
677         luxel[ 0 ] = pass;
678         luxel[ 3 ] = 1.0f;
679         
680         /* add to count */
681         numLuxelsMapped++;
682         
683         /* return ok */
684         return (*cluster);
685 }
686
687
688
689 /*
690 MapTriangle_r()
691 recursively subdivides a triangle until its edges are shorter
692 than the distance between two luxels (thanks jc :)
693 */
694
695 static void MapTriangle_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], vec4_t plane, vec3_t stv[ 3 ], vec3_t ttv[ 3 ], vec3_t worldverts[ 3 ] )
696 {
697         bspDrawVert_t   mid, *dv2[ 3 ];
698         int                             max;
699         
700         
701         /* map the vertexes */
702         #if 0
703         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
704         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
705         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
706         #endif
707         
708         /* subdivide calc */
709         {
710                 int                     i;
711                 float           *a, *b, dx, dy, dist, maxDist;
712                 
713                 
714                 /* find the longest edge and split it */
715                 max = -1;
716                 maxDist = 0;
717                 for( i = 0; i < 3; i++ )
718                 {
719                         /* get verts */
720                         a = dv[ i ]->lightmap[ 0 ];
721                         b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
722                         
723                         /* get dists */
724                         dx = a[ 0 ] - b[ 0 ];
725                         dy = a[ 1 ] - b[ 1 ];
726                         dist = (dx * dx) + (dy * dy);   //% sqrt( (dx * dx) + (dy * dy) );
727                         
728                         /* longer? */
729                         if( dist > maxDist )
730                         {
731                                 maxDist = dist;
732                                 max = i;
733                         }
734                 }
735                 
736                 /* try to early out */
737                 if( max < 0 || maxDist <= subdivideThreshold )  /* ydnar: was i < 0 instead of max < 0 (?) */
738                         return;
739         }
740         
741         /* split the longest edge and map it */
742         LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );
743         MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv, worldverts );
744         
745         /* push the point up a little bit to account for fp creep (fixme: revisit this) */
746         //%     VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
747         
748         /* recurse to first triangle */
749         VectorCopy( dv, dv2 );
750         dv2[ max ] = &mid;
751         MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
752         
753         /* recurse to second triangle */
754         VectorCopy( dv, dv2 );
755         dv2[ (max + 1) % 3 ] = &mid;
756         MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
757 }
758
759
760
761 /*
762 MapTriangle()
763 seed function for MapTriangle_r()
764 requires a cw ordered triangle
765 */
766
767 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )
768 {
769         int                             i;
770         vec4_t                  plane;
771         vec3_t                  *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
772         vec3_t                  worldverts[ 3 ];
773         
774         
775         /* get plane if possible */
776         if( lm->plane != NULL )
777         {
778                 VectorCopy( lm->plane, plane );
779                 plane[ 3 ] = lm->plane[ 3 ];
780         }
781         
782         /* otherwise make one from the points */
783         else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
784                 return qfalse;
785         
786         /* check to see if we need to calculate texture->world tangent vectors */
787         if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )
788         {
789                 stv = stvStatic;
790                 ttv = ttvStatic;
791         }
792         else
793         {
794                 stv = NULL;
795                 ttv = NULL;
796         }
797         
798         VectorCopy( dv[ 0 ]->xyz, worldverts[ 0 ] );
799         VectorCopy( dv[ 1 ]->xyz, worldverts[ 1 ] );
800         VectorCopy( dv[ 2 ]->xyz, worldverts[ 2 ] );
801
802         /* map the vertexes */
803         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, worldverts );
804         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, worldverts );
805         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, worldverts );
806         
807         /* 2002-11-20: prefer axial triangle edges */
808         if( mapNonAxial )
809         {
810                 /* subdivide the triangle */
811                 MapTriangle_r( lm, info, dv, plane, stv, ttv, worldverts );
812                 return qtrue;
813         }
814         
815         for( i = 0; i < 3; i++ )
816         {
817                 float                   *a, *b;
818                 bspDrawVert_t   *dv2[ 3 ];
819                 
820                 
821                 /* get verts */
822                 a = dv[ i ]->lightmap[ 0 ];
823                 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];
824                 
825                 /* make degenerate triangles for mapping edges */
826                 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )
827                 {
828                         dv2[ 0 ] = dv[ i ];
829                         dv2[ 1 ] = dv[ (i + 1) % 3 ];
830                         dv2[ 2 ] = dv[ (i + 1) % 3 ];
831                         
832                         /* map the degenerate triangle */
833                         MapTriangle_r( lm, info, dv2, plane, stv, ttv, worldverts );
834                 }
835         }
836         
837         return qtrue;
838 }
839
840
841
842 /*
843 MapQuad_r()
844 recursively subdivides a quad until its edges are shorter
845 than the distance between two luxels
846 */
847
848 static void MapQuad_r( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ], vec4_t plane, vec3_t stv[ 4 ], vec3_t ttv[ 4 ] )
849 {
850         bspDrawVert_t   mid[ 2 ], *dv2[ 4 ];
851         int                             max;
852         
853         
854         /* subdivide calc */
855         {
856                 int                     i;
857                 float           *a, *b, dx, dy, dist, maxDist;
858                 
859                 
860                 /* find the longest edge and split it */
861                 max = -1;
862                 maxDist = 0;
863                 for( i = 0; i < 4; i++ )
864                 {
865                         /* get verts */
866                         a = dv[ i ]->lightmap[ 0 ];
867                         b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];
868                         
869                         /* get dists */
870                         dx = a[ 0 ] - b[ 0 ];
871                         dy = a[ 1 ] - b[ 1 ];
872                         dist = (dx * dx) + (dy * dy);   //% sqrt( (dx * dx) + (dy * dy) );
873                         
874                         /* longer? */
875                         if( dist > maxDist )
876                         {
877                                 maxDist = dist;
878                                 max = i;
879                         }
880                 }
881                 
882                 /* try to early out */
883                 if( max < 0 || maxDist <= subdivideThreshold )
884                         return;
885         }
886         
887         /* we only care about even/odd edges */
888         max &= 1;
889         
890         /* split the longest edges */
891         LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );
892         LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );
893         
894         /* map the vertexes */
895         MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv, NULL );
896         MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv, NULL );
897         
898         /* 0 and 2 */
899         if( max == 0 )
900         {
901                 /* recurse to first quad */
902                 dv2[ 0 ] = dv[ 0 ];
903                 dv2[ 1 ] = &mid[ 0 ];
904                 dv2[ 2 ] = &mid[ 1 ];
905                 dv2[ 3 ] = dv[ 3 ];
906                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
907                 
908                 /* recurse to second quad */
909                 dv2[ 0 ] = &mid[ 0 ];
910                 dv2[ 1 ] = dv[ 1 ];
911                 dv2[ 2 ] = dv[ 2 ];
912                 dv2[ 3 ] = &mid[ 1 ];
913                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
914         }
915         
916         /* 1 and 3 */
917         else
918         {
919                 /* recurse to first quad */
920                 dv2[ 0 ] = dv[ 0 ];
921                 dv2[ 1 ] = dv[ 1 ];
922                 dv2[ 2 ] = &mid[ 0 ];
923                 dv2[ 3 ] = &mid[ 1 ];
924                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
925                 
926                 /* recurse to second quad */
927                 dv2[ 0 ] = &mid[ 1 ];
928                 dv2[ 1 ] = &mid[ 0 ];
929                 dv2[ 2 ] = dv[ 2 ];
930                 dv2[ 3 ] = dv[ 3 ];
931                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
932         }
933 }
934
935
936
937 /*
938 MapQuad()
939 seed function for MapQuad_r()
940 requires a cw ordered triangle quad
941 */
942
943 #define QUAD_PLANAR_EPSILON             0.5f
944
945 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )
946 {
947         float                   dist;
948         vec4_t                  plane;
949         vec3_t                  *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
950         
951         
952         /* get plane if possible */
953         if( lm->plane != NULL )
954         {
955                 VectorCopy( lm->plane, plane );
956                 plane[ 3 ] = lm->plane[ 3 ];
957         }
958         
959         /* otherwise make one from the points */
960         else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )
961                 return qfalse;
962         
963         /* 4th point must fall on the plane */
964         dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
965         if( fabs( dist ) > QUAD_PLANAR_EPSILON )
966                 return qfalse;
967         
968         /* check to see if we need to calculate texture->world tangent vectors */
969         if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )
970         {
971                 stv = stvStatic;
972                 ttv = ttvStatic;
973         }
974         else
975         {
976                 stv = NULL;
977                 ttv = NULL;
978         }
979         
980         /* map the vertexes */
981         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv, NULL );
982         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv, NULL );
983         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv, NULL );
984         MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv, NULL );
985         
986         /* subdivide the quad */
987         MapQuad_r( lm, info, dv, plane, stv, ttv );
988         return qtrue;
989 }
990
991
992
993 /*
994 MapRawLightmap()
995 maps the locations, normals, and pvs clusters for a raw lightmap
996 */
997
998 #define VectorDivide( in, d, out )      VectorScale( in, (1.0f / (d)), out )    //%     (out)[ 0 ] = (in)[ 0 ] / (d), (out)[ 1 ] = (in)[ 1 ] / (d), (out)[ 2 ] = (in)[ 2 ] / (d)
999
1000 void MapRawLightmap( int rawLightmapNum )
1001 {
1002         int                                     n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
1003         float                           *luxel, *origin, *normal, samples, radius, pass;
1004         rawLightmap_t           *lm;
1005         bspDrawSurface_t        *ds;
1006         surfaceInfo_t           *info;
1007         mesh_t                          src, *subdivided, *mesh;
1008         bspDrawVert_t           *verts, *dv[ 4 ], fake;
1009         
1010         
1011         /* bail if this number exceeds the number of raw lightmaps */
1012         if( rawLightmapNum >= numRawLightmaps )
1013                 return;
1014         
1015         /* get lightmap */
1016         lm = &rawLightmaps[ rawLightmapNum ];
1017         
1018         /* -----------------------------------------------------------------
1019            map referenced surfaces onto the raw lightmap
1020            ----------------------------------------------------------------- */
1021         
1022         /* walk the list of surfaces on this raw lightmap */
1023         for( n = 0; n < lm->numLightSurfaces; n++ )
1024         {
1025                 /* with > 1 surface per raw lightmap, clear occluded */
1026                 if( n > 0 )
1027                 {
1028                         for( y = 0; y < lm->sh; y++ )
1029                         {
1030                                 for( x = 0; x < lm->sw; x++ )
1031                                 {
1032                                         /* get cluster */
1033                                         cluster = SUPER_CLUSTER( x, y );
1034                                         if( *cluster < 0 )
1035                                                 *cluster = CLUSTER_UNMAPPED;
1036                                 }
1037                         }
1038                 }
1039                 
1040                 /* get surface */
1041                 num = lightSurfaces[ lm->firstLightSurface + n ];
1042                 ds = &bspDrawSurfaces[ num ];
1043                 info = &surfaceInfos[ num ];
1044                 
1045                 /* bail if no lightmap to calculate */
1046                 if( info->lm != lm )
1047                 {
1048                         Sys_Printf( "!" );
1049                         continue;
1050                 }
1051                 
1052                 /* map the surface onto the lightmap origin/cluster/normal buffers */
1053                 switch( ds->surfaceType )
1054                 {
1055                         case MST_PLANAR:
1056                                 /* get verts */
1057                                 verts = yDrawVerts + ds->firstVert;
1058                                 
1059                                 /* map the triangles */
1060                                 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1061                                 {
1062                                         for( i = 0; i < ds->numIndexes; i += 3 )
1063                                         {
1064                                                 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
1065                                                 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
1066                                                 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
1067                                                 MapTriangle( lm, info, dv, mapNonAxial );
1068                                         }
1069                                 }
1070                                 break;
1071                         
1072                         case MST_PATCH:
1073                                 /* make a mesh from the drawsurf */ 
1074                                 src.width = ds->patchWidth;
1075                                 src.height = ds->patchHeight;
1076                                 src.verts = &yDrawVerts[ ds->firstVert ];
1077                                 //%     subdivided = SubdivideMesh( src, 8, 512 );
1078                                 subdivided = SubdivideMesh2( src, info->patchIterations );
1079                                 
1080                                 /* fit it to the curve and remove colinear verts on rows/columns */
1081                                 PutMeshOnCurve( *subdivided );
1082                                 mesh = RemoveLinearMeshColumnsRows( subdivided );
1083                                 FreeMesh( subdivided );
1084                                 
1085                                 /* get verts */
1086                                 verts = mesh->verts;
1087                                 
1088                                 /* debug code */
1089                                 #if 0
1090                                         if( lm->plane )
1091                                         {
1092                                                 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1093                                                         lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1094                                                         lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1095                                                         lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1096                                         }
1097                                 #endif
1098                                 
1099                                 /* map the mesh quads */
1100                                 #if 0
1101
1102                                 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1103                                 {
1104                                         for( y = 0; y < (mesh->height - 1); y++ )
1105                                         {
1106                                                 for( x = 0; x < (mesh->width - 1); x++ )
1107                                                 {
1108                                                         /* set indexes */
1109                                                         pw[ 0 ] = x + (y * mesh->width);
1110                                                         pw[ 1 ] = x + ((y + 1) * mesh->width);
1111                                                         pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1112                                                         pw[ 3 ] = x + 1 + (y * mesh->width);
1113                                                         pw[ 4 ] = x + (y * mesh->width);        /* same as pw[ 0 ] */
1114                                                         
1115                                                         /* set radix */
1116                                                         r = (x + y) & 1;
1117                                                         
1118                                                         /* get drawverts and map first triangle */
1119                                                         dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1120                                                         dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1121                                                         dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1122                                                         MapTriangle( lm, info, dv, mapNonAxial );
1123                                                         
1124                                                         /* get drawverts and map second triangle */
1125                                                         dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1126                                                         dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1127                                                         dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1128                                                         MapTriangle( lm, info, dv, mapNonAxial );
1129                                                 }
1130                                         }
1131                                 }
1132                                 
1133                                 #else
1134                                 
1135                                 for( y = 0; y < (mesh->height - 1); y++ )
1136                                 {
1137                                         for( x = 0; x < (mesh->width - 1); x++ )
1138                                         {
1139                                                 /* set indexes */
1140                                                 pw[ 0 ] = x + (y * mesh->width);
1141                                                 pw[ 1 ] = x + ((y + 1) * mesh->width);
1142                                                 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);
1143                                                 pw[ 3 ] = x + 1 + (y * mesh->width);
1144                                                 pw[ 4 ] = pw[ 0 ];
1145                                                 
1146                                                 /* set radix */
1147                                                 r = (x + y) & 1;
1148                                                 
1149                                                 /* attempt to map quad first */
1150                                                 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1151                                                 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1152                                                 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1153                                                 dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1154                                                 if( MapQuad( lm, info, dv ) )
1155                                                         continue;
1156                                                 
1157                                                 /* get drawverts and map first triangle */
1158                                                 MapTriangle( lm, info, dv, mapNonAxial );
1159                                                 
1160                                                 /* get drawverts and map second triangle */
1161                                                 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1162                                                 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1163                                                 MapTriangle( lm, info, dv, mapNonAxial );
1164                                         }
1165                                 }
1166                                 
1167                                 #endif
1168                                 
1169                                 /* free the mesh */
1170                                 FreeMesh( mesh );
1171                                 break;
1172                         
1173                         default:
1174                                 break;
1175                 }
1176         }
1177         
1178         /* -----------------------------------------------------------------
1179            average and clean up luxel normals
1180            ----------------------------------------------------------------- */
1181         
1182         /* walk the luxels */
1183         for( y = 0; y < lm->sh; y++ )
1184         {
1185                 for( x = 0; x < lm->sw; x++ )
1186                 {
1187                         /* get luxel */
1188                         luxel = SUPER_LUXEL( 0, x, y );
1189                         normal = SUPER_NORMAL( x, y );
1190                         cluster = SUPER_CLUSTER( x, y );
1191
1192                         /* only look at mapped luxels */
1193                         if( *cluster < 0 )
1194                                 continue;
1195                         
1196                         /* the normal data could be the sum of multiple samples */
1197                         if( luxel[ 3 ] > 1.0f )
1198                                 VectorNormalize( normal, normal );
1199                         
1200                         /* mark this luxel as having only one normal */
1201                         luxel[ 3 ] = 1.0f;
1202                 }
1203         }
1204         
1205         /* non-planar surfaces stop here */
1206         if( lm->plane == NULL )
1207                 return;
1208         
1209         /* -----------------------------------------------------------------
1210            map occluded or unuxed luxels
1211            ----------------------------------------------------------------- */
1212         
1213         /* walk the luxels */
1214         radius = floor( superSample / 2 );
1215         radius = radius > 0 ? radius : 1.0f;
1216         radius += 1.0f;
1217         for( pass = 2.0f; pass <= radius; pass += 1.0f )
1218         {
1219                 for( y = 0; y < lm->sh; y++ )
1220                 {
1221                         for( x = 0; x < lm->sw; x++ )
1222                         {
1223                                 /* get luxel */
1224                                 luxel = SUPER_LUXEL( 0, x, y );
1225                                 normal = SUPER_NORMAL( x, y );
1226                                 cluster = SUPER_CLUSTER( x, y );
1227                                 
1228                                 /* only look at unmapped luxels */
1229                                 if( *cluster != CLUSTER_UNMAPPED )
1230                                         continue;
1231                                 
1232                                 /* divine a normal and origin from neighboring luxels */
1233                                 VectorClear( fake.xyz );
1234                                 VectorClear( fake.normal );
1235                                 fake.lightmap[ 0 ][ 0 ] = x;    //% 0.0001 + x;
1236                                 fake.lightmap[ 0 ][ 1 ] = y;    //% 0.0001 + y;
1237                                 samples = 0.0f;
1238                                 for( sy = (y - 1); sy <= (y + 1); sy++ )
1239                                 {
1240                                         if( sy < 0 || sy >= lm->sh )
1241                                                 continue;
1242                                         
1243                                         for( sx = (x - 1); sx <= (x + 1); sx++ )
1244                                         {
1245                                                 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1246                                                         continue;
1247                                                 
1248                                                 /* get neighboring luxel */
1249                                                 luxel = SUPER_LUXEL( 0, sx, sy );
1250                                                 origin = SUPER_ORIGIN( sx, sy );
1251                                                 normal = SUPER_NORMAL( sx, sy );
1252                                                 cluster = SUPER_CLUSTER( sx, sy );
1253                                                 
1254                                                 /* only consider luxels mapped in previous passes */
1255                                                 if( *cluster < 0 || luxel[ 0 ] >= pass )
1256                                                         continue;
1257                                                 
1258                                                 /* add its distinctiveness to our own */
1259                                                 VectorAdd( fake.xyz, origin, fake.xyz );
1260                                                 VectorAdd( fake.normal, normal, fake.normal );
1261                                                 samples += luxel[ 3 ];
1262                                         }
1263                                 }
1264                                 
1265                                 /* any samples? */
1266                                 if( samples == 0.0f )
1267                                         continue;
1268                                 
1269                                 /* average */
1270                                 VectorDivide( fake.xyz, samples, fake.xyz );
1271                                 //%     VectorDivide( fake.normal, samples, fake.normal );
1272                                 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )
1273                                         continue;
1274                                 
1275                                 /* map the fake vert */
1276                                 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL, NULL );
1277                         }
1278                 }
1279         }
1280         
1281         /* -----------------------------------------------------------------
1282            average and clean up luxel normals
1283            ----------------------------------------------------------------- */
1284         
1285         /* walk the luxels */
1286         for( y = 0; y < lm->sh; y++ )
1287         {
1288                 for( x = 0; x < lm->sw; x++ )
1289                 {
1290                         /* get luxel */
1291                         luxel = SUPER_LUXEL( 0, x, y );
1292                         normal = SUPER_NORMAL( x, y );
1293                         cluster = SUPER_CLUSTER( x, y );
1294                         
1295                         /* only look at mapped luxels */
1296                         if( *cluster < 0 )
1297                                 continue;
1298                         
1299                         /* the normal data could be the sum of multiple samples */
1300                         if( luxel[ 3 ] > 1.0f )
1301                                 VectorNormalize( normal, normal );
1302                         
1303                         /* mark this luxel as having only one normal */
1304                         luxel[ 3 ] = 1.0f;
1305                 }
1306         }
1307         
1308         /* debug code */
1309         #if 0
1310                 Sys_Printf( "\n" );
1311                 for( y = 0; y < lm->sh; y++ )
1312                 {
1313                         for( x = 0; x < lm->sw; x++ )
1314                         {
1315                                 vec3_t  mins, maxs;
1316                                 
1317
1318                                 cluster = SUPER_CLUSTER( x, y );
1319                                 origin = SUPER_ORIGIN( x, y );
1320                                 normal = SUPER_NORMAL( x, y );
1321                                 luxel = SUPER_LUXEL( x, y );
1322                                 
1323                                 if( *cluster < 0 )
1324                                         continue;
1325                                 
1326                                 /* check if within the bounding boxes of all surfaces referenced */
1327                                 ClearBounds( mins, maxs );
1328                                 for( n = 0; n < lm->numLightSurfaces; n++ )
1329                                 {
1330                                         int TOL;
1331                                         info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1332                                         TOL = info->sampleSize + 2;
1333                                         AddPointToBounds( info->mins, mins, maxs );
1334                                         AddPointToBounds( info->maxs, mins, maxs );
1335                                         if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&
1336                                                 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&
1337                                                 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )
1338                                                 break;
1339                                 }
1340                                 
1341                                 /* inside? */
1342                                 if( n < lm->numLightSurfaces )
1343                                         continue;
1344                                 
1345                                 /* report bogus origin */
1346                                 Sys_Printf( "%6d [%2d,%2d] (%4d): XYZ(%+4.1f %+4.1f %+4.1f) LO(%+4.1f %+4.1f %+4.1f) HI(%+4.1f %+4.1f %+4.1f) <%3.0f>\n",
1347                                         rawLightmapNum, x, y, *cluster,
1348                                         origin[ 0 ], origin[ 1 ], origin[ 2 ],
1349                                         mins[ 0 ], mins[ 1 ], mins[ 2 ],
1350                                         maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1351                                         luxel[ 3 ] );
1352                         }
1353                 }
1354         #endif
1355 }
1356
1357
1358
1359 /*
1360 SetupDirt()
1361 sets up dirtmap (ambient occlusion)
1362 */
1363
1364 #define DIRT_CONE_ANGLE                         88      /* degrees */
1365 #define DIRT_NUM_ANGLE_STEPS            16
1366 #define DIRT_NUM_ELEVATION_STEPS        3
1367 #define DIRT_NUM_VECTORS                        (DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS)
1368
1369 static vec3_t           dirtVectors[ DIRT_NUM_VECTORS ];
1370 static int                      numDirtVectors = 0;
1371
1372 void SetupDirt( void )
1373 {
1374         int             i, j;
1375         float   angle, elevation, angleStep, elevationStep;
1376         
1377         
1378         /* note it */
1379         Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1380         
1381         /* calculate angular steps */
1382         angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1383         elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1384         
1385         /* iterate angle */
1386         angle = 0.0f;
1387         for( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1388         {
1389                 /* iterate elevation */
1390                 for( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1391                 {
1392                         dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1393                         dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1394                         dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1395                         numDirtVectors++;
1396                 }
1397         }
1398         
1399         /* emit some statistics */
1400         Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1401 }
1402
1403
1404 /*
1405 DirtForSample()
1406 calculates dirt value for a given sample
1407 */
1408
1409 float DirtForSample( trace_t *trace )
1410 {
1411         int             i;
1412         float   gatherDirt, outDirt, angle, elevation, ooDepth;
1413         vec3_t  normal, worldUp, myUp, myRt, temp, direction, displacement;
1414         
1415         
1416         /* dummy check */
1417         if( !dirty )
1418                 return 1.0f;
1419         if( trace == NULL || trace->cluster < 0 )
1420                 return 0.0f;
1421         
1422         /* setup */
1423         gatherDirt = 0.0f;
1424         ooDepth = 1.0f / dirtDepth;
1425         VectorCopy( trace->normal, normal );
1426         
1427         /* check if the normal is aligned to the world-up */
1428         if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
1429         {
1430                 if( normal[ 2 ] == 1.0f )               
1431                 {
1432                         VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1433                         VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1434                 }
1435                 else if( normal[ 2 ] == -1.0f )
1436                 {
1437                         VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1438                         VectorSet( myUp,  0.0f, 1.0f, 0.0f );
1439                 }
1440         }
1441         else
1442         {
1443                 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1444                 CrossProduct( normal, worldUp, myRt );
1445                 VectorNormalize( myRt, myRt );
1446                 CrossProduct( myRt, normal, myUp );
1447                 VectorNormalize( myUp, myUp );
1448         }
1449         
1450         /* 1 = random mode, 0 (well everything else) = non-random mode */
1451         if( dirtMode == 1 )
1452         {
1453                 /* iterate */
1454                 for( i = 0; i < numDirtVectors; i++ )
1455                 {
1456                         /* get random vector */
1457                         angle = Random() * DEG2RAD( 360.0f );
1458                         elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1459                         temp[ 0 ] = cos( angle ) * sin( elevation );
1460                         temp[ 1 ] = sin( angle ) * sin( elevation );
1461                         temp[ 2 ] = cos( elevation );
1462                         
1463                         /* transform into tangent space */
1464                         direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1465                         direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1466                         direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1467                         
1468                         /* set endpoint */
1469                         VectorMA( trace->origin, dirtDepth, direction, trace->end );
1470                         SetupTrace( trace );
1471                         
1472                         /* trace */
1473                         TraceLine( trace );
1474                         if( trace->opaque )
1475                         {
1476                                 VectorSubtract( trace->hit, trace->origin, displacement );
1477                                 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1478                         }
1479                 }
1480         }
1481         else
1482         {
1483                 /* iterate through ordered vectors */
1484                 for( i = 0; i < numDirtVectors; i++ )
1485                 {
1486                         /* transform vector into tangent space */
1487                         direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1488                         direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1489                         direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1490                         
1491                         /* set endpoint */
1492                         VectorMA( trace->origin, dirtDepth, direction, trace->end );
1493                         SetupTrace( trace );
1494                         
1495                         /* trace */
1496                         TraceLine( trace );
1497                         if( trace->opaque )
1498                         {
1499                                 VectorSubtract( trace->hit, trace->origin, displacement );
1500                                 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1501                         }
1502                 }
1503         }
1504         
1505         /* direct ray */
1506         VectorMA( trace->origin, dirtDepth, normal, trace->end );
1507         SetupTrace( trace );
1508         
1509         /* trace */
1510         TraceLine( trace );
1511         if( trace->opaque )
1512         {
1513                 VectorSubtract( trace->hit, trace->origin, displacement );
1514                 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1515         }
1516         
1517         /* early out */
1518         if( gatherDirt <= 0.0f )
1519                 return 1.0f;
1520         
1521         /* apply gain (does this even do much? heh) */
1522         outDirt = pow( gatherDirt / (numDirtVectors + 1), dirtGain );
1523         if( outDirt > 1.0f )
1524                 outDirt = 1.0f;
1525         
1526         /* apply scale */
1527         outDirt *= dirtScale;
1528         if( outDirt > 1.0f )
1529                 outDirt = 1.0f;
1530         
1531         /* return to sender */
1532         return 1.0f - outDirt;
1533 }
1534
1535
1536
1537 /*
1538 DirtyRawLightmap()
1539 calculates dirty fraction for each luxel
1540 */
1541
1542 void DirtyRawLightmap( int rawLightmapNum )
1543 {
1544         int                                     i, x, y, sx, sy, *cluster;
1545         float                           *origin, *normal, *dirt, *dirt2, average, samples;
1546         rawLightmap_t           *lm;
1547         surfaceInfo_t           *info;
1548         trace_t                         trace;
1549         qboolean                        noDirty;
1550
1551         
1552         /* bail if this number exceeds the number of raw lightmaps */
1553         if( rawLightmapNum >= numRawLightmaps )
1554                 return;
1555         
1556         /* get lightmap */
1557         lm = &rawLightmaps[ rawLightmapNum ];
1558         
1559         /* setup trace */
1560         trace.testOcclusion = qtrue;
1561         trace.forceSunlight = qfalse;
1562         trace.recvShadows = lm->recvShadows;
1563         trace.numSurfaces = lm->numLightSurfaces;
1564         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1565         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1566         trace.testAll = qtrue;
1567         
1568         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1569         trace.twoSided = qfalse;
1570         for( i = 0; i < trace.numSurfaces; i++ )
1571         {
1572                 /* get surface */
1573                 info = &surfaceInfos[ trace.surfaces[ i ] ];
1574                 
1575                 /* check twosidedness */
1576                 if( info->si->twoSided )
1577                 {
1578                         trace.twoSided = qtrue;
1579                         break;
1580                 }
1581         }
1582
1583         noDirty = qfalse;
1584         for( i = 0; i < trace.numSurfaces; i++ )
1585         {
1586                 /* get surface */
1587                 info = &surfaceInfos[ trace.surfaces[ i ] ];
1588
1589                 /* check twosidedness */
1590                 if( info->si->noDirty )
1591                 {
1592                         noDirty = qtrue;
1593                         break;
1594                 }
1595         }
1596         
1597         /* gather dirt */
1598         for( y = 0; y < lm->sh; y++ )
1599         {
1600                 for( x = 0; x < lm->sw; x++ )
1601                 {
1602                         /* get luxel */
1603                         cluster = SUPER_CLUSTER( x, y );
1604                         origin = SUPER_ORIGIN( x, y );
1605                         normal = SUPER_NORMAL( x, y );
1606                         dirt = SUPER_DIRT( x, y );
1607                         
1608                         /* set default dirt */
1609                         *dirt = 0.0f;
1610                         
1611                         /* only look at mapped luxels */
1612                         if( *cluster < 0 )
1613                                 continue;
1614
1615                         /* don't apply dirty on this surface */
1616                         if( noDirty )
1617                         {
1618                                 *dirt = 1.0f;
1619                                 continue;
1620                         }
1621                         
1622                         /* copy to trace */
1623                         trace.cluster = *cluster;
1624                         VectorCopy( origin, trace.origin );
1625                         VectorCopy( normal, trace.normal );
1626                         
1627                         /* get dirt */
1628                         *dirt = DirtForSample( &trace );
1629                 }
1630         }
1631         
1632         /* testing no filtering */
1633         //%     return;
1634         
1635         /* filter dirt */
1636         for( y = 0; y < lm->sh; y++ )
1637         {
1638                 for( x = 0; x < lm->sw; x++ )
1639                 {
1640                         /* get luxel */
1641                         cluster = SUPER_CLUSTER( x, y );
1642                         dirt = SUPER_DIRT( x, y );
1643                         
1644                         /* filter dirt by adjacency to unmapped luxels */
1645                         average = *dirt;
1646                         samples = 1.0f;
1647                         for( sy = (y - 1); sy <= (y + 1); sy++ )
1648                         {
1649                                 if( sy < 0 || sy >= lm->sh )
1650                                         continue;
1651                                 
1652                                 for( sx = (x - 1); sx <= (x + 1); sx++ )
1653                                 {
1654                                         if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
1655                                                 continue;
1656                                         
1657                                         /* get neighboring luxel */
1658                                         cluster = SUPER_CLUSTER( sx, sy );
1659                                         dirt2 = SUPER_DIRT( sx, sy );
1660                                         if( *cluster < 0 || *dirt2 <= 0.0f )
1661                                                 continue;
1662                                         
1663                                         /* add it */
1664                                         average += *dirt2;
1665                                         samples += 1.0f;
1666                                 }
1667                                 
1668                                 /* bail */
1669                                 if( samples <= 0.0f )
1670                                         break;
1671                         }
1672                         
1673                         /* bail */
1674                         if( samples <= 0.0f )
1675                                 continue;
1676                         
1677                         /* scale dirt */
1678                         *dirt = average / samples;
1679                 }
1680         }
1681 }
1682
1683
1684
1685 /*
1686 SubmapRawLuxel()
1687 calculates the pvs cluster, origin, normal of a sub-luxel
1688 */
1689
1690 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )
1691 {
1692         int                     i, *cluster, *cluster2;
1693         float           *origin, *origin2, *normal;     //%     , *normal2;
1694         vec3_t          originVecs[ 2 ];                        //%     , normalVecs[ 2 ];
1695         
1696         
1697         /* calulate x vector */
1698         if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )
1699         {
1700                 cluster = SUPER_CLUSTER( x, y );
1701                 origin = SUPER_ORIGIN( x, y );
1702                 //%     normal = SUPER_NORMAL( x, y );
1703                 cluster2 = SUPER_CLUSTER( x + 1, y );
1704                 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1705                 //%     normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1706         }
1707         else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )
1708         {
1709                 cluster = SUPER_CLUSTER( x - 1, y );
1710                 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1711                 //%     normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1712                 cluster2 = SUPER_CLUSTER( x, y );
1713                 origin2 = SUPER_ORIGIN( x, y );
1714                 //%     normal2 = SUPER_NORMAL( x, y );
1715         }
1716         else
1717                 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1718         
1719         VectorSubtract( origin2, origin, originVecs[ 0 ] );
1720         //%     VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1721         
1722         /* calulate y vector */
1723         if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )
1724         {
1725                 cluster = SUPER_CLUSTER( x, y );
1726                 origin = SUPER_ORIGIN( x, y );
1727                 //%     normal = SUPER_NORMAL( x, y );
1728                 cluster2 = SUPER_CLUSTER( x, y + 1 );
1729                 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1730                 //%     normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1731         }
1732         else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )
1733         {
1734                 cluster = SUPER_CLUSTER( x, y - 1 );
1735                 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1736                 //%     normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1737                 cluster2 = SUPER_CLUSTER( x, y );
1738                 origin2 = SUPER_ORIGIN( x, y );
1739                 //%     normal2 = SUPER_NORMAL( x, y );
1740         }
1741         else
1742                 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1743         
1744         VectorSubtract( origin2, origin, originVecs[ 1 ] );
1745         //%     VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1746         
1747         /* calculate new origin */
1748         //%     VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1749         //%     VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1750         for( i = 0; i < 3; i++ )
1751                 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);
1752         
1753         /* get cluster */
1754         *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );
1755         if( *sampleCluster < 0 )
1756                 return qfalse;
1757         
1758         /* calculate new normal */
1759         //%     VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1760         //%     VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1761         //%     if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1762         //%             return qfalse;
1763         normal = SUPER_NORMAL( x, y );
1764         VectorCopy( normal, sampleNormal );
1765         
1766         /* return ok */
1767         return qtrue;
1768 }
1769
1770
1771 /*
1772 SubsampleRawLuxel_r()
1773 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1774 */
1775
1776 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )
1777 {
1778         int                     b, samples, mapped, lighted;
1779         int                     cluster[ 4 ];
1780         vec4_t          luxel[ 4 ];
1781         vec3_t          origin[ 4 ], normal[ 4 ];
1782         float           biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1783         vec3_t          color, total;
1784         
1785         
1786         /* limit check */
1787         if( lightLuxel[ 3 ] >= lightSamples )
1788                 return;
1789         
1790         /* setup */
1791         VectorClear( total );
1792         mapped = 0;
1793         lighted = 0;
1794         
1795         /* make 2x2 subsample stamp */
1796         for( b = 0; b < 4; b++ )
1797         {
1798                 /* set origin */
1799                 VectorCopy( sampleOrigin, origin[ b ] );
1800                 
1801                 /* calculate position */
1802                 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )
1803                 {
1804                         cluster[ b ] = -1;
1805                         continue;
1806                 }
1807                 mapped++;
1808                 
1809                 /* increment sample count */
1810                 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1811                 
1812                 /* setup trace */
1813                 trace->cluster = *cluster;
1814                 VectorCopy( origin[ b ], trace->origin );
1815                 VectorCopy( normal[ b ], trace->normal );
1816                 
1817                 /* sample light */
1818
1819                 LightContributionToSample( trace );
1820                 if(trace->forceSubsampling > 1.0f)
1821                 {
1822                         /* alphashadow: we subsample as deep as we can */
1823                         ++lighted;
1824                         ++mapped;
1825                         ++mapped;
1826                 }
1827                 
1828                 /* add to totals (fixme: make contrast function) */
1829                 VectorCopy( trace->color, luxel[ b ] );
1830                 VectorAdd( total, trace->color, total );
1831                 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )
1832                         lighted++;
1833         }
1834         
1835         /* subsample further? */
1836         if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&
1837                 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&
1838                 lighted != 0 && lighted != mapped )
1839         {
1840                 for( b = 0; b < 4; b++ )
1841                 {
1842                         if( cluster[ b ] < 0 )
1843                                 continue;
1844                         SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.5f), luxel[ b ] );
1845                 }
1846         }
1847         
1848         /* average */
1849         //%     VectorClear( color );
1850         //%     samples = 0;
1851         VectorCopy( lightLuxel, color );
1852         samples = 1;
1853         for( b = 0; b < 4; b++ )
1854         {
1855                 if( cluster[ b ] < 0 )
1856                         continue;
1857                 VectorAdd( color, luxel[ b ], color );
1858                 samples++;
1859         }
1860         
1861         /* add to luxel */
1862         if( samples > 0 )
1863         {
1864                 /* average */
1865                 color[ 0 ] /= samples;
1866                 color[ 1 ] /= samples;
1867                 color[ 2 ] /= samples;
1868                 
1869                 /* add to color */
1870                 VectorCopy( color, lightLuxel );
1871                 lightLuxel[ 3 ] += 1.0f;
1872         }
1873 }
1874
1875
1876
1877 /*
1878 IlluminateRawLightmap()
1879 illuminates the luxels
1880 */
1881
1882 #define STACK_LL_SIZE                   (SUPER_LUXEL_SIZE * 64 * 64)
1883 #define LIGHT_LUXEL( x, y )             (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))
1884
1885 void IlluminateRawLightmap( int rawLightmapNum )
1886 {
1887         int                                     i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1888         int                                     *cluster, *cluster2, mapped, lighted, totalLighted;
1889         rawLightmap_t           *lm;
1890         surfaceInfo_t           *info;
1891         qboolean                        filterColor, filterDir;
1892         float                           brightness;
1893         float                           *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1894         unsigned char                   *flag;
1895         float                           *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1896         vec3_t                          color, averageColor, averageDir, total, temp, temp2;
1897         float                           tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1898         trace_t                         trace;
1899         float                           stackLightLuxels[ STACK_LL_SIZE ];
1900         
1901         
1902         /* bail if this number exceeds the number of raw lightmaps */
1903         if( rawLightmapNum >= numRawLightmaps )
1904                 return;
1905         
1906         /* get lightmap */
1907         lm = &rawLightmaps[ rawLightmapNum ];
1908         
1909         /* setup trace */
1910         trace.testOcclusion = !noTrace;
1911         trace.forceSunlight = qfalse;
1912         trace.recvShadows = lm->recvShadows;
1913         trace.numSurfaces = lm->numLightSurfaces;
1914         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1915         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1916         
1917         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1918         trace.twoSided = qfalse;
1919         for( i = 0; i < trace.numSurfaces; i++ )
1920         {
1921                 /* get surface */
1922                 info = &surfaceInfos[ trace.surfaces[ i ] ];
1923                 
1924                 /* check twosidedness */
1925                 if( info->si->twoSided )
1926                 {
1927                         trace.twoSided = qtrue;
1928                         break;
1929                 }
1930         }
1931         
1932         /* create a culled light list for this raw lightmap */
1933         CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1934         
1935         /* -----------------------------------------------------------------
1936            fill pass
1937            ----------------------------------------------------------------- */
1938         
1939         /* set counts */
1940         numLuxelsIlluminated += (lm->sw * lm->sh);
1941         
1942         /* test debugging state */
1943         if( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap )
1944         {
1945                 /* debug fill the luxels */
1946                 for( y = 0; y < lm->sh; y++ )
1947                 {
1948                         for( x = 0; x < lm->sw; x++ )
1949                         {
1950                                 /* get cluster */
1951                                 cluster = SUPER_CLUSTER( x, y );
1952
1953                                 /* only fill mapped luxels */
1954                                 if( *cluster < 0 )
1955                                         continue;
1956                                 
1957                                 /* get particulars */
1958                                 luxel = SUPER_LUXEL( 0, x, y );
1959                                 origin = SUPER_ORIGIN( x, y );
1960                                 normal = SUPER_NORMAL( x, y );
1961                                 
1962                                 /* color the luxel with raw lightmap num? */
1963                                 if( debugSurfaces )
1964                                         VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1965                                 
1966                                 /* color the luxel with lightmap axis? */
1967                                 else if( debugAxis )
1968                                 {
1969                                         luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;
1970                                         luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;
1971                                         luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;
1972                                 }
1973                                 
1974                                 /* color the luxel with luxel cluster? */
1975                                 else if( debugCluster )
1976                                         VectorCopy( debugColors[ *cluster % 12 ], luxel );
1977                                 
1978                                 /* color the luxel with luxel origin? */
1979                                 else if( debugOrigin )
1980                                 {
1981                                         VectorSubtract( lm->maxs, lm->mins, temp );
1982                                         VectorScale( temp, (1.0f / 255.0f), temp );
1983                                         VectorSubtract( origin, lm->mins, temp2 );
1984                                         luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
1985                                         luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
1986                                         luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
1987                                 }
1988                                 
1989                                 /* color the luxel with the normal */
1990                                 else if( normalmap )
1991                                 {
1992                                         luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;
1993                                         luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;
1994                                         luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;
1995                                 }
1996                                 
1997                                 /* otherwise clear it */
1998                                 else
1999                                         VectorClear( luxel );
2000                                 
2001                                 /* add to counts */
2002                                 luxel[ 3 ] = 1.0f;
2003                         }
2004                 }
2005         }
2006         else
2007         {
2008                 /* allocate temporary per-light luxel storage */
2009                 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2010                 if( llSize <= (STACK_LL_SIZE * sizeof( float )) )
2011                         lightLuxels = stackLightLuxels;
2012                 else
2013                         lightLuxels = safe_malloc( llSize );
2014                 
2015                 /* clear luxels */
2016                 //%     memset( lm->superLuxels[ 0 ], 0, llSize );
2017                 
2018                 /* set ambient color */
2019                 for( y = 0; y < lm->sh; y++ )
2020                 {
2021                         for( x = 0; x < lm->sw; x++ )
2022                         {
2023                                 /* get cluster */
2024                                 cluster = SUPER_CLUSTER( x, y );
2025                                 luxel = SUPER_LUXEL( 0, x, y );
2026                                 normal = SUPER_NORMAL( x, y );
2027                                 deluxel = SUPER_DELUXEL( x, y );
2028                                 
2029                                 /* blacken unmapped clusters */
2030                                 if( *cluster < 0 )
2031                                         VectorClear( luxel );
2032                                 
2033                                 /* set ambient */
2034                                 else
2035                                 {
2036                                         VectorCopy( ambientColor, luxel );
2037                                         if( deluxemap )
2038                                         {
2039                                                 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f/255.0f );
2040
2041                                                 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2042                                                 if(brightness < 0.00390625f)
2043                                                         brightness = 0.00390625f;
2044
2045                                                 VectorScale( normal, brightness, deluxel );
2046                                         }
2047                                         luxel[ 3 ] = 1.0f;
2048                                 }
2049                         }
2050                 }
2051                 
2052                 /* clear styled lightmaps */
2053                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2054                 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2055                 {
2056                         if( lm->superLuxels[ lightmapNum ] != NULL )
2057                                 memset( lm->superLuxels[ lightmapNum ], 0, size );
2058                 }
2059                 
2060                 /* debugging code */
2061                 //%     if( trace.numLights <= 0 )
2062                 //%             Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2063                 
2064                 /* walk light list */
2065                 for( i = 0; i < trace.numLights; i++ )
2066                 {
2067                         /* setup trace */
2068                         trace.light = trace.lights[ i ];
2069                         
2070                         /* style check */
2071                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2072                         {
2073                                 if( lm->styles[ lightmapNum ] == trace.light->style ||
2074                                         lm->styles[ lightmapNum ] == LS_NONE )
2075                                         break;
2076                         }
2077                         
2078                         /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2079                         if( lightmapNum >= MAX_LIGHTMAPS )
2080                         {
2081                                 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2082                                 continue;
2083                         }
2084                         
2085                         /* setup */
2086                         memset( lightLuxels, 0, llSize );
2087                         totalLighted = 0;
2088                         
2089                         /* determine filter radius */
2090                         filterRadius = lm->filterRadius > trace.light->filterRadius
2091                                 ? lm->filterRadius
2092                                 : trace.light->filterRadius;
2093                         if( filterRadius < 0.0f )
2094                                 filterRadius = 0.0f;
2095                         
2096                         /* set luxel filter radius */
2097                         luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2098                         if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )
2099                                 luxelFilterRadius = 1;
2100
2101                         /* allocate sampling flags storage */
2102                         if(lightSamples > 1 && luxelFilterRadius == 0)
2103                         {
2104                                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2105                                 if(lm->superFlags == NULL)
2106                                         lm->superFlags = safe_malloc( size );
2107                                 memset( (void *) lm->superFlags, 0, size );
2108                         }
2109
2110                         /* initial pass, one sample per luxel */
2111                         for( y = 0; y < lm->sh; y++ )
2112                         {
2113                                 for( x = 0; x < lm->sw; x++ )
2114                                 {
2115                                         /* get cluster */
2116                                         cluster = SUPER_CLUSTER( x, y );
2117                                         if( *cluster < 0 )
2118                                                 continue;
2119                                         
2120                                         /* get particulars */
2121                                         lightLuxel = LIGHT_LUXEL( x, y );
2122                                         deluxel = SUPER_DELUXEL( x, y );
2123                                         origin = SUPER_ORIGIN( x, y );
2124                                         normal = SUPER_NORMAL( x, y );
2125                                         flag = SUPER_FLAG( x, y );
2126
2127 #if 0
2128                                         ////////// 27's temp hack for testing edge clipping ////
2129                                         if( origin[0]==0 && origin[1]==0 && origin[2]==0 )
2130                                         {
2131                                                 lightLuxel[ 1 ] = 255;
2132                                                 lightLuxel[ 3 ] = 1.0f;
2133                                                 totalLighted++;
2134                                         }
2135                                         else
2136 #endif
2137                                         {
2138                                                 /* set contribution count */
2139                                                 lightLuxel[ 3 ] = 1.0f;
2140
2141                                                 /* setup trace */
2142                                                 trace.cluster = *cluster;
2143                                                 VectorCopy( origin, trace.origin );
2144                                                 VectorCopy( normal, trace.normal );
2145
2146                                                 /* get light for this sample */
2147                                                 LightContributionToSample( &trace );
2148                                                 VectorCopy( trace.color, lightLuxel );
2149
2150                                                 /* add the contribution to the deluxemap */
2151                                                 if( deluxemap )
2152                                                         VectorAdd( deluxel, trace.directionContribution, deluxel );
2153
2154                                                 /* check for evilness */
2155                                                 if(trace.forceSubsampling > 1.0f && lightSamples > 1 && luxelFilterRadius == 0)
2156                                                 {
2157                                                         totalLighted++;
2158                                                         *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2159                                                 }
2160                                                 /* add to count */
2161                                                 else if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )
2162                                                         totalLighted++;
2163                                         }
2164                                 }
2165                         }
2166                         
2167                         /* don't even bother with everything else if nothing was lit */
2168                         if( totalLighted == 0 )
2169                                 continue;
2170                         
2171                         /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2172                         /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2173                         if( lightSamples > 1 && luxelFilterRadius == 0 )
2174                         {
2175                                 /* walk luxels */
2176                                 for( y = 0; y < (lm->sh - 1); y++ )
2177                                 {
2178                                         for( x = 0; x < (lm->sw - 1); x++ )
2179                                         {
2180                                                 /* setup */
2181                                                 mapped = 0;
2182                                                 lighted = 0;
2183                                                 VectorClear( total );
2184                                                 
2185                                                 /* test 2x2 stamp */
2186                                                 for( t = 0; t < 4; t++ )
2187                                                 {
2188                                                         /* set sample coords */
2189                                                         sx = x + tests[ t ][ 0 ];
2190                                                         sy = y + tests[ t ][ 1 ];
2191                                                         
2192                                                         /* get cluster */
2193                                                         cluster = SUPER_CLUSTER( sx, sy );
2194                                                         if( *cluster < 0 )
2195                                                                 continue;
2196                                                         mapped++;
2197                                                         
2198                                                         /* get luxel */
2199                                                         flag = SUPER_FLAG( sx, sy );
2200                                                         if(*flag & FLAG_FORCE_SUBSAMPLING)
2201                                                         {
2202                                                                 /* force a lighted/mapped discrepancy so we subsample */
2203                                                                 ++lighted;
2204                                                                 ++mapped;
2205                                                                 ++mapped;
2206                                                         }
2207                                                         lightLuxel = LIGHT_LUXEL( sx, sy );
2208                                                         VectorAdd( total, lightLuxel, total );
2209                                                         if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )
2210                                                                 lighted++;
2211                                                 }
2212                                                 
2213                                                 /* if total color is under a certain amount, then don't bother subsampling */
2214                                                 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )
2215                                                         continue;
2216                                                 
2217                                                 /* if all 4 pixels are either in shadow or light, then don't subsample */
2218                                                 if( lighted != 0 && lighted != mapped )
2219                                                 {
2220                                                         for( t = 0; t < 4; t++ )
2221                                                         {
2222                                                                 /* set sample coords */
2223                                                                 sx = x + tests[ t ][ 0 ];
2224                                                                 sy = y + tests[ t ][ 1 ];
2225                                                                 
2226                                                                 /* get luxel */
2227                                                                 cluster = SUPER_CLUSTER( sx, sy );
2228                                                                 if( *cluster < 0 )
2229                                                                         continue;
2230                                                                 flag = SUPER_FLAG( sx, sy );
2231                                                                 if(*flag & FLAG_ALREADY_SUBSAMPLED) // already subsampled
2232                                                                         continue;
2233                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );
2234                                                                 origin = SUPER_ORIGIN( sx, sy );
2235                                                                 
2236                                                                 /* only subsample shadowed luxels */
2237                                                                 //%     if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2238                                                                 //%             continue;
2239                                                                 
2240                                                                 /* subsample it */
2241                                                                 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel );
2242
2243                                                                 *flag |= FLAG_ALREADY_SUBSAMPLED;
2244                                                                 
2245                                                                 /* debug code to colorize subsampled areas to yellow */
2246                                                                 //%     luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2247                                                                 //%     VectorSet( luxel, 255, 204, 0 );
2248                                                         }
2249                                                 }
2250                                         }
2251                                 }
2252                         }
2253                         
2254                         /* tertiary pass, apply dirt map (ambient occlusion) */
2255                         if( 0 && dirty )
2256                         {
2257                                 /* walk luxels */
2258                                 for( y = 0; y < lm->sh; y++ )
2259                                 {
2260                                         for( x = 0; x < lm->sw; x++ )
2261                                         {
2262                                                 /* get cluster  */
2263                                                 cluster = SUPER_CLUSTER( x, y );
2264                                                 if( *cluster < 0 )
2265                                                         continue;
2266                                                 
2267                                                 /* get particulars */
2268                                                 lightLuxel = LIGHT_LUXEL( x, y );
2269                                                 dirt = SUPER_DIRT( x, y );
2270                                                 
2271                                                 /* scale light value */
2272                                                 VectorScale( lightLuxel, *dirt, lightLuxel );
2273                                         }
2274                                 }
2275                         }
2276                         
2277                         /* allocate sampling lightmap storage */
2278                         if( lm->superLuxels[ lightmapNum ] == NULL )
2279                         {
2280                                 /* allocate sampling lightmap storage */
2281                                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2282                                 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2283                                 memset( lm->superLuxels[ lightmapNum ], 0, size );
2284                         }
2285
2286                         /* set style */
2287                         if( lightmapNum > 0 )
2288                         {
2289                                 lm->styles[ lightmapNum ] = trace.light->style;
2290                                 //%     Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2291                         }
2292                         
2293                         /* copy to permanent luxels */
2294                         for( y = 0; y < lm->sh; y++ )
2295                         {
2296                                 for( x = 0; x < lm->sw; x++ )
2297                                 {
2298                                         /* get cluster and origin */
2299                                         cluster = SUPER_CLUSTER( x, y );
2300                                         if( *cluster < 0 )
2301                                                 continue;
2302                                         origin = SUPER_ORIGIN( x, y );
2303                                         
2304                                         /* filter? */
2305                                         if( luxelFilterRadius )
2306                                         {
2307                                                 /* setup */
2308                                                 VectorClear( averageColor );
2309                                                 samples = 0.0f;
2310                                                 
2311                                                 /* cheaper distance-based filtering */
2312                                                 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )
2313                                                 {
2314                                                         if( sy < 0 || sy >= lm->sh )
2315                                                                 continue;
2316                                                         
2317                                                         for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )
2318                                                         {
2319                                                                 if( sx < 0 || sx >= lm->sw )
2320                                                                         continue;
2321                                                                 
2322                                                                 /* get particulars */
2323                                                                 cluster = SUPER_CLUSTER( sx, sy );
2324                                                                 if( *cluster < 0 )
2325                                                                         continue;
2326                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );
2327                                                                 
2328                                                                 /* create weight */
2329                                                                 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);
2330                                                                 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);
2331                                                                 
2332                                                                 /* scale luxel by filter weight */
2333                                                                 VectorScale( lightLuxel, weight, color );
2334                                                                 VectorAdd( averageColor, color, averageColor );
2335                                                                 samples += weight;
2336                                                         }
2337                                                 }
2338                                                 
2339                                                 /* any samples? */
2340                                                 if( samples <= 0.0f     )
2341                                                         continue;
2342                                                 
2343                                                 /* scale into luxel */
2344                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2345                                                 luxel[ 3 ] = 1.0f;
2346                                                 
2347                                                 /* handle negative light */
2348                                                 if( trace.light->flags & LIGHT_NEGATIVE )
2349                                                 { 
2350                                                         luxel[ 0 ] -= averageColor[ 0 ] / samples;
2351                                                         luxel[ 1 ] -= averageColor[ 1 ] / samples;
2352                                                         luxel[ 2 ] -= averageColor[ 2 ] / samples;
2353                                                 }
2354                                                 
2355                                                 /* handle normal light */
2356                                                 else
2357                                                 { 
2358                                                         luxel[ 0 ] += averageColor[ 0 ] / samples;
2359                                                         luxel[ 1 ] += averageColor[ 1 ] / samples;
2360                                                         luxel[ 2 ] += averageColor[ 2 ] / samples;
2361                                                 }
2362                                         }
2363                                         
2364                                         /* single sample */
2365                                         else
2366                                         {
2367                                                 /* get particulars */
2368                                                 lightLuxel = LIGHT_LUXEL( x, y );
2369                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2370                                                 
2371                                                 /* handle negative light */
2372                                                 if( trace.light->flags & LIGHT_NEGATIVE )
2373                                                         VectorScale( averageColor, -1.0f, averageColor );
2374
2375                                                 /* add color */
2376                                                 luxel[ 3 ] = 1.0f;
2377                                                 
2378                                                 /* handle negative light */
2379                                                 if( trace.light->flags & LIGHT_NEGATIVE )
2380                                                         VectorSubtract( luxel, lightLuxel, luxel );
2381                                                 
2382                                                 /* handle normal light */
2383                                                 else
2384                                                         VectorAdd( luxel, lightLuxel, luxel );
2385                                         }
2386                                 }
2387                         }
2388                 }
2389                 
2390                 /* free temporary luxels */
2391                 if( lightLuxels != stackLightLuxels )
2392                         free( lightLuxels );
2393         }
2394         
2395         /* free light list */
2396         FreeTraceLights( &trace );
2397         
2398         /* floodlight pass */
2399         if( floodlighty )
2400                 FloodlightIlluminateLightmap(lm);
2401
2402         if (debugnormals)
2403         {
2404                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2405                 {
2406                         /* early out */
2407                         if( lm->superLuxels[ lightmapNum ] == NULL )
2408                                 continue;
2409                         
2410                         for( y = 0; y < lm->sh; y++ )
2411                         {
2412                                 for( x = 0; x < lm->sw; x++ )
2413                                 {
2414                                         /* get cluster */
2415                                         cluster = SUPER_CLUSTER( x, y );
2416                                         //%     if( *cluster < 0 )
2417                                         //%             continue;
2418                                         
2419                                         /* get particulars */
2420                                         luxel = SUPER_LUXEL( lightmapNum, x, y );
2421                                         normal = SUPER_NORMAL (  x, y );
2422                
2423                                         luxel[0]=(normal[0]*127)+127;
2424                                         luxel[1]=(normal[1]*127)+127;
2425                                         luxel[2]=(normal[2]*127)+127;
2426                                 }
2427                         }
2428                 }
2429         }
2430         
2431         /*      -----------------------------------------------------------------
2432                 dirt pass
2433                 ----------------------------------------------------------------- */
2434         
2435         if( dirty )
2436         {
2437                 /* walk lightmaps */
2438                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2439                 {
2440                         /* early out */
2441                         if( lm->superLuxels[ lightmapNum ] == NULL )
2442                                 continue;
2443                         
2444                         /* apply dirt to each luxel */
2445                         for( y = 0; y < lm->sh; y++ )
2446                         {
2447                                 for( x = 0; x < lm->sw; x++ )
2448                                 {
2449                                         /* get cluster */
2450                                         cluster = SUPER_CLUSTER( x, y );
2451                                         //%     if( *cluster < 0 ) // TODO why not do this check? These pixels should be zero anyway
2452                                         //%             continue;
2453                                         
2454                                         /* get particulars */
2455                                         luxel = SUPER_LUXEL( lightmapNum, x, y );
2456                                         dirt = SUPER_DIRT( x, y );
2457                                         
2458                                         /* apply dirt */
2459                                         VectorScale( luxel, *dirt, luxel );
2460                                         
2461                                         /* debugging */
2462                                         if( dirtDebug )
2463                                                 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2464                                 }
2465                         }
2466                 }
2467         }
2468         
2469         /* -----------------------------------------------------------------
2470            filter pass
2471            ----------------------------------------------------------------- */
2472         
2473         /* walk lightmaps */
2474         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2475         {
2476                 /* early out */
2477                 if( lm->superLuxels[ lightmapNum ] == NULL )
2478                         continue;
2479                 
2480                 /* average occluded luxels from neighbors */
2481                 for( y = 0; y < lm->sh; y++ )
2482                 {
2483                         for( x = 0; x < lm->sw; x++ )
2484                         {
2485                                 /* get particulars */
2486                                 cluster = SUPER_CLUSTER( x, y );
2487                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2488                                 deluxel = SUPER_DELUXEL( x, y );
2489                                 normal = SUPER_NORMAL( x, y );
2490                                 
2491                                 /* determine if filtering is necessary */
2492                                 filterColor = qfalse;
2493                                 filterDir = qfalse;
2494                                 if( *cluster < 0 ||
2495                                         (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )
2496                                         filterColor = qtrue;
2497
2498                                 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )
2499                                         filterDir = qtrue;
2500                                 
2501                                 if( !filterColor && !filterDir )
2502                                         continue;
2503                                 
2504                                 /* choose seed amount */
2505                                 VectorClear( averageColor );
2506                                 VectorClear( averageDir );
2507                                 samples = 0.0f;
2508                                 
2509                                 /* walk 3x3 matrix */
2510                                 for( sy = (y - 1); sy <= (y + 1); sy++ )
2511                                 {
2512                                         if( sy < 0 || sy >= lm->sh )
2513                                                 continue;
2514                                         
2515                                         for( sx = (x - 1); sx <= (x + 1); sx++ )
2516                                         {
2517                                                 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
2518                                                         continue;
2519                                                 
2520                                                 /* get neighbor's particulars */
2521                                                 cluster2 = SUPER_CLUSTER( sx, sy );
2522                                                 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2523                                                 deluxel2 = SUPER_DELUXEL( sx, sy );
2524                                                 
2525                                                 /* ignore unmapped/unlit luxels */
2526                                                 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2527                                                         (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )
2528                                                         continue;
2529                                                 
2530                                                 /* add its distinctiveness to our own */
2531                                                 VectorAdd( averageColor, luxel2, averageColor );
2532                                                 samples += luxel2[ 3 ];
2533                                                 if( filterDir )
2534                                                         VectorAdd( averageDir, deluxel2, averageDir );
2535                                         }
2536                                 }
2537                                 
2538                                 /* fall through */
2539                                 if( samples <= 0.0f )
2540                                         continue;
2541                                 
2542                                 /* dark lightmap seams */
2543                                 if( dark )
2544                                 {
2545                                         if( lightmapNum == 0 )
2546                                                 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2547                                         samples += 2.0f;
2548                                 }
2549                                 
2550                                 /* average it */
2551                                 if( filterColor )
2552                                 {
2553                                         VectorDivide( averageColor, samples, luxel );
2554                                         luxel[ 3 ] = 1.0f;
2555                                 }
2556                                 if( filterDir )
2557                                         VectorDivide( averageDir, samples, deluxel );
2558                                 
2559                                 /* set cluster to -3 */
2560                                 if( *cluster < 0 )
2561                                         *cluster = CLUSTER_FLOODED;
2562                         }
2563                 }
2564         }
2565
2566
2567 #if 0
2568         // audit pass
2569         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2570         {
2571                 /* early out */
2572                 if( lm->superLuxels[ lightmapNum ] == NULL )
2573                         continue;
2574                 for( y = 0; y < lm->sh; y++ )
2575                         for( x = 0; x < lm->sw; x++ )
2576                         {
2577                                 /* get cluster */
2578                                 cluster = SUPER_CLUSTER( x, y );
2579                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2580                                 deluxel = SUPER_DELUXEL( x, y );
2581                                 if(!luxel || !deluxel || !cluster)
2582                                 {
2583                                         Sys_FPrintf(SYS_VRB, "WARNING: I got NULL'd.\n");
2584                                         continue;
2585                                 }
2586                                 else if(*cluster < 0)
2587                                 {
2588                                         // unmapped pixel
2589                                         // should have neither deluxemap nor lightmap
2590                                         if(deluxel[3])
2591                                                 Sys_FPrintf(SYS_VRB, "WARNING: I have written deluxe to an unmapped luxel. Sorry.\n");
2592                                 }
2593                                 else
2594                                 {
2595                                         // mapped pixel
2596                                         // should have both deluxemap and lightmap
2597                                         if(deluxel[3])
2598                                                 Sys_FPrintf(SYS_VRB, "WARNING: I forgot to write deluxe to a mapped luxel. Sorry.\n");
2599                                 }
2600                         }
2601         }
2602 #endif
2603 }
2604
2605
2606
2607 /*
2608 IlluminateVertexes()
2609 light the surface vertexes
2610 */
2611
2612 #define VERTEX_NUDGE    4.0f
2613
2614 void IlluminateVertexes( int num )
2615 {
2616         int                                     i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2617         int                                     lightmapNum, numAvg;
2618         float                           samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2619         vec3_t                          origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2620         bspDrawSurface_t        *ds;
2621         surfaceInfo_t           *info;
2622         rawLightmap_t           *lm;
2623         bspDrawVert_t           *verts;
2624         trace_t                         trace;
2625         float                           floodLightAmount;
2626         vec3_t                          floodColor;
2627         
2628         
2629         /* get surface, info, and raw lightmap */
2630         ds = &bspDrawSurfaces[ num ];
2631         info = &surfaceInfos[ num ];
2632         lm = info->lm;
2633         
2634         /* -----------------------------------------------------------------
2635            illuminate the vertexes
2636            ----------------------------------------------------------------- */
2637         
2638         /* calculate vertex lighting for surfaces without lightmaps */
2639         if( lm == NULL || cpmaHack )
2640         {
2641                 /* setup trace */
2642                 trace.testOcclusion = (cpmaHack && lm != NULL) ? qfalse : !noTrace;
2643                 trace.forceSunlight = info->si->forceSunlight;
2644                 trace.recvShadows = info->recvShadows;
2645                 trace.numSurfaces = 1;
2646                 trace.surfaces = &num;
2647                 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2648                 
2649                 /* twosided lighting */
2650                 trace.twoSided = info->si->twoSided;
2651                 
2652                 /* make light list for this surface */
2653                 CreateTraceLightsForSurface( num, &trace );
2654                 
2655                 /* setup */
2656                 verts = yDrawVerts + ds->firstVert;
2657                 numAvg = 0;
2658                 memset( avgColors, 0, sizeof( avgColors ) );
2659                 
2660                 /* walk the surface verts */
2661                 for( i = 0; i < ds->numVerts; i++ )
2662                 {
2663                         /* get vertex luxel */
2664                         radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2665                         
2666                         /* color the luxel with raw lightmap num? */
2667                         if( debugSurfaces )
2668                                 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2669                         
2670                         /* color the luxel with luxel origin? */
2671                         else if( debugOrigin )
2672                         {
2673                                 VectorSubtract( info->maxs, info->mins, temp );
2674                                 VectorScale( temp, (1.0f / 255.0f), temp );
2675                                 VectorSubtract( origin, lm->mins, temp2 );
2676                                 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);
2677                                 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);
2678                                 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);
2679                         }
2680                         
2681                         /* color the luxel with the normal */
2682                         else if( normalmap )
2683                         {
2684                                 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2685                                 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2686                                 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2687                         }
2688                         
2689                         /* illuminate the vertex */
2690                         else
2691                         {
2692                                 /* clear vertex luxel */
2693                                 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2694                                 
2695                                 /* try at initial origin */
2696                                 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2697                                 if( trace.cluster >= 0 )
2698                                 {
2699                                         /* setup trace */
2700                                         VectorCopy( verts[ i ].xyz, trace.origin );
2701                                         VectorCopy( verts[ i ].normal, trace.normal );
2702                                         
2703                                         /* r7 dirt */
2704                                         if( dirty && !bouncing )
2705                                                 dirt = DirtForSample( &trace );
2706                                         else
2707                                                 dirt = 1.0f;
2708
2709                                         /* jal: floodlight */
2710                                         floodLightAmount = 0.0f;
2711                                         VectorClear( floodColor );
2712                                         if( floodlighty && !bouncing )
2713                                         {
2714                                                 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2715                                                 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2716                                         }
2717
2718                                         /* trace */
2719                                         LightingAtSample( &trace, ds->vertexStyles, colors );
2720                                         
2721                                         /* store */
2722                                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2723                                         {
2724                                                 /* r7 dirt */
2725                                                 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2726
2727                                                 /* jal: floodlight */
2728                                                 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] ); 
2729                                                 
2730                                                 /* store */
2731                                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2732                                                 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2733                                                 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2734                                         }
2735                                 }
2736                                 
2737                                 /* is this sample bright enough? */
2738                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2739                                 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2740                                         radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2741                                         radVertLuxel[ 2 ] <= ambientColor[ 2 ] )
2742                                 {
2743                                         /* nudge the sample point around a bit */
2744                                         for( x = 0; x < 4; x++ )
2745                                         {
2746                                                 /* two's complement 0, 1, -1, 2, -2, etc */
2747                                                 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
2748                                                 
2749                                                 for( y = 0; y < 4; y++ )
2750                                                 {
2751                                                         y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
2752                                                         
2753                                                         for( z = 0; z < 4; z++ )
2754                                                         {
2755                                                                 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
2756                                                                 
2757                                                                 /* nudge origin */
2758                                                                 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);
2759                                                                 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);
2760                                                                 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);
2761                                                                 
2762                                                                 /* try at nudged origin */
2763                                                                 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2764                                                                 if( trace.cluster < 0 )
2765                                                                         continue;
2766                                                                                                                         
2767                                                                 /* trace */
2768                                                                 LightingAtSample( &trace, ds->vertexStyles, colors );
2769                                                                 
2770                                                                 /* store */
2771                                                                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2772                                                                 {
2773                                                                         /* r7 dirt */
2774                                                                         VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2775
2776                                                                         /* jal: floodlight */
2777                                                                         VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] ); 
2778                                                                         
2779                                                                         /* store */
2780                                                                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2781                                                                         VectorCopy( colors[ lightmapNum ], radVertLuxel );
2782                                                                 }
2783                                                                 
2784                                                                 /* bright enough? */
2785                                                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2786                                                                 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2787                                                                         radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2788                                                                         radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2789                                                                         x = y = z = 1000;
2790                                                         }
2791                                                 }
2792                                         }
2793                                 }
2794                                 
2795                                 /* add to average? */
2796                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2797                                 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2798                                         radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2799                                         radVertLuxel[ 2 ] > ambientColor[ 2 ] )
2800                                 {
2801                                         numAvg++;
2802                                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2803                                         {
2804                                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2805                                                 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2806                                         }
2807                                 }
2808                         }
2809                         
2810                         /* another happy customer */
2811                         numVertsIlluminated++;
2812                 }
2813                 
2814                 /* set average color */
2815                 if( numAvg > 0 )
2816                 {
2817                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2818                                 VectorScale( avgColors[ lightmapNum ], (1.0f / numAvg), avgColors[ lightmapNum ] );
2819                 }
2820                 else
2821                 {
2822                         VectorCopy( ambientColor, avgColors[ 0 ] );
2823                 }
2824                 
2825                 /* clean up and store vertex color */
2826                 for( i = 0; i < ds->numVerts; i++ )
2827                 {
2828                         /* get vertex luxel */
2829                         radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2830                         
2831                         /* store average in occluded vertexes */
2832                         if( radVertLuxel[ 0 ] < 0.0f )
2833                         {
2834                                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2835                                 {
2836                                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2837                                         VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2838                                         
2839                                         /* debug code */
2840                                         //%     VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2841                                 }
2842                         }
2843                         
2844                         /* store it */
2845                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2846                         {
2847                                 /* get luxels */
2848                                 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2849                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2850                                 
2851                                 /* store */
2852                                 if( bouncing || bounce == 0 || !bounceOnly )
2853                                         VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2854                                 if( !info->si->noVertexLight )
2855                                         ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2856                         }
2857                 }
2858                 
2859                 /* free light list */
2860                 FreeTraceLights( &trace );
2861                 
2862                 /* return to sender */
2863                 return;
2864         }
2865         
2866         /* -----------------------------------------------------------------
2867            reconstitute vertex lighting from the luxels
2868            ----------------------------------------------------------------- */
2869         
2870         /* set styles from lightmap */
2871         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2872                 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2873         
2874         /* get max search radius */
2875         maxRadius = lm->sw;
2876         maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2877         
2878         /* walk the surface verts */
2879         verts = yDrawVerts + ds->firstVert;
2880         for( i = 0; i < ds->numVerts; i++ )
2881         {
2882                 /* do each lightmap */
2883                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2884                 {
2885                         /* early out */
2886                         if( lm->superLuxels[ lightmapNum ] == NULL )
2887                                 continue;
2888                         
2889                         /* get luxel coords */
2890                         x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2891                         y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2892                         if( x < 0 )
2893                                 x = 0;
2894                         else if( x >= lm->sw )
2895                                 x = lm->sw - 1;
2896                         if( y < 0 )
2897                                 y = 0;
2898                         else if( y >= lm->sh )
2899                                 y = lm->sh - 1;
2900                         
2901                         /* get vertex luxels */
2902                         vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2903                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2904                         
2905                         /* color the luxel with the normal? */
2906                         if( normalmap )
2907                         {
2908                                 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;
2909                                 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;
2910                                 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;
2911                         }
2912                         
2913                         /* color the luxel with surface num? */
2914                         else if( debugSurfaces )
2915                                 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2916                         
2917                         /* divine color from the superluxels */
2918                         else
2919                         {
2920                                 /* increasing radius */
2921                                 VectorClear( radVertLuxel );
2922                                 samples = 0.0f;
2923                                 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
2924                                 {
2925                                         /* sample within radius */
2926                                         for( sy = (y - radius); sy <= (y + radius); sy++ )
2927                                         {
2928                                                 if( sy < 0 || sy >= lm->sh )
2929                                                         continue;
2930                                                 
2931                                                 for( sx = (x - radius); sx <= (x + radius); sx++ )
2932                                                 {
2933                                                         if( sx < 0 || sx >= lm->sw )
2934                                                                 continue;
2935                                                         
2936                                                         /* get luxel particulars */
2937                                                         luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2938                                                         cluster = SUPER_CLUSTER( sx, sy );
2939                                                         if( *cluster < 0 )
2940                                                                 continue;
2941                                                         
2942                                                         /* testing: must be brigher than ambient color */
2943                                                         //%     if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
2944                                                         //%             continue;
2945                                                         
2946                                                         /* add its distinctiveness to our own */
2947                                                         VectorAdd( radVertLuxel, luxel, radVertLuxel );
2948                                                         samples += luxel[ 3 ];
2949                                                 }
2950                                         }
2951                                 }
2952                                 
2953                                 /* any color? */
2954                                 if( samples > 0.0f )
2955                                         VectorDivide( radVertLuxel, samples, radVertLuxel );
2956                                 else
2957                                         VectorCopy( ambientColor, radVertLuxel );
2958                         }
2959                         
2960                         /* store into floating point storage */
2961                         VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2962                         numVertsIlluminated++;
2963                         
2964                         /* store into bytes (for vertex approximation) */
2965                         if( !info->si->noVertexLight )
2966                                 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
2967                 }
2968         }
2969 }
2970
2971
2972
2973 /* -------------------------------------------------------------------------------
2974
2975 light optimization (-fast)
2976
2977 creates a list of lights that will affect a surface and stores it in tw
2978 this is to optimize surface lighting by culling out as many of the
2979 lights in the world as possible from further calculation
2980
2981 ------------------------------------------------------------------------------- */
2982
2983 /*
2984 SetupBrushes()
2985 determines opaque brushes in the world and find sky shaders for sunlight calculations
2986 */
2987
2988 void SetupBrushes( void )
2989 {
2990         int                             i, j, b, compileFlags;
2991         qboolean                inside;
2992         bspBrush_t              *brush;
2993         bspBrushSide_t  *side;
2994         bspShader_t             *shader;
2995         shaderInfo_t    *si;
2996         
2997         
2998         /* note it */
2999         Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3000         
3001         /* allocate */
3002         if( opaqueBrushes == NULL )
3003                 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3004         
3005         /* clear */
3006         memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3007         numOpaqueBrushes = 0;
3008         
3009         /* walk the list of worldspawn brushes */
3010         for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3011         {
3012                 /* get brush */
3013                 b = bspModels[ 0 ].firstBSPBrush + i;
3014                 brush = &bspBrushes[ b ];
3015                 
3016                 /* check all sides */
3017                 inside = qtrue;
3018                 compileFlags = 0;
3019                 for( j = 0; j < brush->numSides && inside; j++ )
3020                 {
3021                         /* do bsp shader calculations */
3022                         side = &bspBrushSides[ brush->firstSide + j ];
3023                         shader = &bspShaders[ side->shaderNum ];
3024                         
3025                         /* get shader info */
3026                         si = ShaderInfoForShader( shader->shader );
3027                         if( si == NULL )
3028                                 continue;
3029                         
3030                         /* or together compile flags */
3031                         compileFlags |= si->compileFlags;
3032                 }
3033                 
3034                 /* determine if this brush is opaque to light */
3035                 if( !(compileFlags & C_TRANSLUCENT) )
3036                 {
3037                         opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));
3038                         numOpaqueBrushes++;
3039                         maxOpaqueBrush = i;
3040                 }
3041         }
3042         
3043         /* emit some statistics */
3044         Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3045 }
3046
3047
3048
3049 /*
3050 ClusterVisible()
3051 determines if two clusters are visible to each other using the PVS
3052 */
3053
3054 qboolean ClusterVisible( int a, int b )
3055 {
3056         int                     portalClusters, leafBytes;
3057         byte            *pvs;
3058         
3059         
3060         /* dummy check */
3061         if( a < 0 || b < 0 )
3062                 return qfalse;
3063         
3064         /* early out */
3065         if( a == b )
3066                 return qtrue;
3067         
3068         /* not vised? */
3069         if( numBSPVisBytes <=8 )
3070                 return qtrue;
3071         
3072         /* get pvs data */
3073         portalClusters = ((int *) bspVisBytes)[ 0 ];
3074         leafBytes = ((int*) bspVisBytes)[ 1 ];
3075         pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);
3076         
3077         /* check */
3078         if( (pvs[ b >> 3 ] & (1 << (b & 7))) )
3079                 return qtrue;
3080         return qfalse;
3081 }
3082
3083
3084
3085 /*
3086 PointInLeafNum_r()
3087 borrowed from vlight.c
3088 */
3089
3090 int     PointInLeafNum_r( vec3_t point, int nodenum )
3091 {
3092         int                     leafnum;
3093         vec_t           dist;
3094         bspNode_t               *node;
3095         bspPlane_t      *plane;
3096         
3097         
3098         while( nodenum >= 0 )
3099         {
3100                 node = &bspNodes[ nodenum ];
3101                 plane = &bspPlanes[ node->planeNum ];
3102                 dist = DotProduct( point, plane->normal ) - plane->dist;
3103                 if( dist > 0.1 )
3104                         nodenum = node->children[ 0 ];
3105                 else if( dist < -0.1 )
3106                         nodenum = node->children[ 1 ];
3107                 else
3108                 {
3109                         leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3110                         if( bspLeafs[ leafnum ].cluster != -1 )
3111                                 return leafnum;
3112                         nodenum = node->children[ 1 ];
3113                 }
3114         }
3115         
3116         leafnum = -nodenum - 1;
3117         return leafnum;
3118 }
3119
3120
3121
3122 /*
3123 PointInLeafnum()
3124 borrowed from vlight.c
3125 */
3126
3127 int     PointInLeafNum( vec3_t point )
3128 {
3129         return PointInLeafNum_r( point, 0 );
3130 }
3131
3132
3133
3134 /*
3135 ClusterVisibleToPoint() - ydnar
3136 returns qtrue if point can "see" cluster
3137 */
3138
3139 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )
3140 {
3141         int             pointCluster;
3142         
3143
3144         /* get leafNum for point */
3145         pointCluster = ClusterForPoint( point );
3146         if( pointCluster < 0 )
3147                 return qfalse;
3148         
3149         /* check pvs */
3150         return ClusterVisible( pointCluster, cluster );
3151 }
3152
3153
3154
3155 /*
3156 ClusterForPoint() - ydnar
3157 returns the pvs cluster for point
3158 */
3159
3160 int ClusterForPoint( vec3_t point )
3161 {
3162         int             leafNum;
3163         
3164
3165         /* get leafNum for point */
3166         leafNum = PointInLeafNum( point );
3167         if( leafNum < 0 )
3168                 return -1;
3169         
3170         /* return the cluster */
3171         return bspLeafs[ leafNum ].cluster;
3172 }
3173
3174
3175
3176 /*
3177 ClusterForPointExt() - ydnar
3178 also takes brushes into account for occlusion testing
3179 */
3180
3181 int ClusterForPointExt( vec3_t point, float epsilon )
3182 {
3183         int                             i, j, b, leafNum, cluster;
3184         float                   dot;
3185         qboolean                inside;
3186         int                             *brushes, numBSPBrushes;
3187         bspLeaf_t               *leaf;
3188         bspBrush_t              *brush;
3189         bspPlane_t              *plane;
3190         
3191         
3192         /* get leaf for point */
3193         leafNum = PointInLeafNum( point );
3194         if( leafNum < 0 )
3195                 return -1;
3196         leaf = &bspLeafs[ leafNum ];
3197         
3198         /* get the cluster */
3199         cluster = leaf->cluster;
3200         if( cluster < 0 )
3201                 return -1;
3202         
3203         /* transparent leaf, so check point against all brushes in the leaf */
3204         brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3205         numBSPBrushes = leaf->numBSPLeafBrushes;
3206         for( i = 0; i < numBSPBrushes; i++ )
3207         {
3208                 /* get parts */
3209                 b = brushes[ i ];
3210                 if( b > maxOpaqueBrush )
3211                         continue;
3212                 brush = &bspBrushes[ b ];
3213                 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )
3214                         continue;
3215                 
3216                 /* check point against all planes */
3217                 inside = qtrue;
3218                 for( j = 0; j < brush->numSides && inside; j++ )
3219                 {
3220                         plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3221                         dot = DotProduct( point, plane->normal );
3222                         dot -= plane->dist;
3223                         if( dot > epsilon )
3224                                 inside = qfalse;
3225                 }
3226                 
3227                 /* if inside, return bogus cluster */
3228                 if( inside )
3229                         return -1 - b;
3230         }
3231         
3232         /* if the point made it this far, it's not inside any opaque brushes */
3233         return cluster;
3234 }
3235
3236
3237
3238 /*
3239 ClusterForPointExtFilter() - ydnar
3240 adds cluster checking against a list of known valid clusters
3241 */
3242
3243 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )
3244 {
3245         int             i, cluster;
3246         
3247         
3248         /* get cluster for point */
3249         cluster = ClusterForPointExt( point, epsilon );
3250         
3251         /* check if filtering is necessary */
3252         if( cluster < 0 || numClusters <= 0 || clusters == NULL )
3253                 return cluster;
3254         
3255         /* filter */
3256         for( i = 0; i < numClusters; i++ )
3257         {
3258                 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )
3259                         return cluster;
3260         }
3261         
3262         /* failed */
3263         return -1;
3264 }
3265
3266
3267
3268 /*
3269 ShaderForPointInLeaf() - ydnar
3270 checks a point against all brushes in a leaf, returning the shader of the brush
3271 also sets the cumulative surface and content flags for the brush hit
3272 */
3273
3274 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )
3275 {
3276         int                             i, j;
3277         float                   dot;
3278         qboolean                inside;
3279         int                             *brushes, numBSPBrushes;
3280         bspLeaf_t                       *leaf;
3281         bspBrush_t              *brush;
3282         bspBrushSide_t  *side;
3283         bspPlane_t              *plane;
3284         bspShader_t             *shader;
3285         int                             allSurfaceFlags, allContentFlags;
3286
3287         
3288         /* clear things out first */
3289         *surfaceFlags = 0;
3290         *contentFlags = 0;
3291         
3292         /* get leaf */
3293         if( leafNum < 0 )
3294                 return -1;
3295         leaf = &bspLeafs[ leafNum ];
3296         
3297         /* transparent leaf, so check point against all brushes in the leaf */
3298         brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3299         numBSPBrushes = leaf->numBSPLeafBrushes;
3300         for( i = 0; i < numBSPBrushes; i++ )
3301         {
3302                 /* get parts */
3303                 brush = &bspBrushes[ brushes[ i ] ];
3304                 
3305                 /* check point against all planes */
3306                 inside = qtrue;
3307                 allSurfaceFlags = 0;
3308                 allContentFlags = 0;
3309                 for( j = 0; j < brush->numSides && inside; j++ )
3310                 {
3311                         side = &bspBrushSides[ brush->firstSide + j ];
3312                         plane = &bspPlanes[ side->planeNum ];
3313                         dot = DotProduct( point, plane->normal );
3314                         dot -= plane->dist;
3315                         if( dot > epsilon )
3316                                 inside = qfalse;
3317                         else
3318                         {
3319                                 shader = &bspShaders[ side->shaderNum ];
3320                                 allSurfaceFlags |= shader->surfaceFlags;
3321                                 allContentFlags |= shader->contentFlags;
3322                         }
3323                 }
3324                 
3325                 /* handle if inside */
3326                 if( inside )
3327                 {
3328                         /* if there are desired flags, check for same and continue if they aren't matched */
3329                         if( wantContentFlags && !(wantContentFlags & allContentFlags) )
3330                                 continue;
3331                         if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )
3332                                 continue;
3333                         
3334                         /* store the cumulative flags and return the brush shader (which is mostly useless) */
3335                         *surfaceFlags = allSurfaceFlags;
3336                         *contentFlags = allContentFlags;
3337                         return brush->shaderNum;
3338                 }
3339         }
3340         
3341         /* if the point made it this far, it's not inside any brushes */
3342         return -1;
3343 }
3344
3345
3346
3347 /*
3348 ChopBounds()
3349 chops a bounding box by the plane defined by origin and normal
3350 returns qfalse if the bounds is entirely clipped away
3351
3352 this is not exactly the fastest way to do this...
3353 */
3354
3355 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )
3356 {
3357         /* FIXME: rewrite this so it doesn't use bloody brushes */
3358         return qtrue;
3359 }
3360
3361
3362
3363 /*
3364 SetupEnvelopes()
3365 calculates each light's effective envelope,
3366 taking into account brightness, type, and pvs.
3367 */
3368
3369 #define LIGHT_EPSILON   0.125f
3370 #define LIGHT_NUDGE             2.0f
3371
3372 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )
3373 {
3374         int                     i, x, y, z, x1, y1, z1;
3375         light_t         *light, *light2, **owner;
3376         bspLeaf_t       *leaf;
3377         vec3_t          origin, dir, mins, maxs, nullVector = { 0, 0, 0 };
3378         float           radius, intensity;
3379         light_t         *buckets[ 256 ];
3380         
3381         
3382         /* early out for weird cases where there are no lights */
3383         if( lights == NULL )
3384                 return;
3385         
3386         /* note it */
3387         Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3388         
3389         /* count lights */
3390         numLights = 0;
3391         numCulledLights = 0;
3392         owner = &lights;
3393         while( *owner != NULL )
3394         {
3395                 /* get light */
3396                 light = *owner;
3397                 
3398                 /* handle negative lights */
3399                 if( light->photons < 0.0f || light->add < 0.0f )
3400                 {
3401                         light->photons *= -1.0f;
3402                         light->add *= -1.0f;
3403                         light->flags |= LIGHT_NEGATIVE;
3404                 }
3405                 
3406                 /* sunlight? */
3407                 if( light->type == EMIT_SUN )
3408                 {
3409                         /* special cased */
3410                         light->cluster = 0;
3411                         light->envelope = MAX_WORLD_COORD * 8.0f;
3412                         VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3413                         VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3414                 }
3415                 
3416                 /* everything else */
3417                 else
3418                 {
3419                         /* get pvs cluster for light */
3420                         light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3421                         
3422                         /* invalid cluster? */
3423                         if( light->cluster < 0 )
3424                         {
3425                                 /* nudge the sample point around a bit */
3426                                 for( x = 0; x < 4; x++ )
3427                                 {
3428                                         /* two's complement 0, 1, -1, 2, -2, etc */
3429                                         x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);
3430                                         
3431                                         for( y = 0; y < 4; y++ )
3432                                         {
3433                                                 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);
3434                                                 
3435                                                 for( z = 0; z < 4; z++ )
3436                                                 {
3437                                                         z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);
3438                                                         
3439                                                         /* nudge origin */
3440                                                         origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);
3441                                                         origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);
3442                                                         origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);
3443                                                         
3444                                                         /* try at nudged origin */
3445                                                         light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3446                                                         if( light->cluster < 0 )
3447                                                                 continue;
3448                                                                         
3449                                                         /* set origin */
3450                                                         VectorCopy( origin, light->origin );
3451                                                 }
3452                                         }
3453                                 }
3454                         }
3455                         
3456                         /* only calculate for lights in pvs and outside of opaque brushes */
3457                         if( light->cluster >= 0 )
3458                         {
3459                                 /* set light fast flag */
3460                                 if( fastFlag )
3461                                         light->flags |= LIGHT_FAST_TEMP;
3462                                 else
3463                                         light->flags &= ~LIGHT_FAST_TEMP;
3464                                 if( light->si && light->si->noFast )
3465                                         light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);
3466                                 
3467                                 /* clear light envelope */
3468                                 light->envelope = 0;
3469                                 
3470                                 /* handle area lights */
3471                                 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )
3472                                 {
3473                                         /* ugly hack to calculate extent for area lights, but only done once */
3474                                         VectorScale( light->normal, -1.0f, dir );
3475                                         for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )
3476                                         {
3477                                                 float   factor;
3478                                                 
3479                                                 VectorMA( light->origin, radius, light->normal, origin );
3480                                                 factor = PointToPolygonFormFactor( origin, dir, light->w );
3481                                                 if( factor < 0.0f )
3482                                                         factor *= -1.0f;
3483                                                 if( (factor * light->add) <= light->falloffTolerance )
3484                                                         light->envelope = radius;
3485                                         }
3486                                         
3487                                         /* check for fast mode */
3488                                         if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )
3489                                                 light->envelope = MAX_WORLD_COORD * 8.0f;
3490                                 }
3491                                 else
3492                                 {
3493                                         radius = 0.0f;
3494                                         intensity = light->photons;
3495                                 }
3496                                 
3497                                 /* other calcs */
3498                                 if( light->envelope <= 0.0f )
3499                                 {
3500                                         /* solve distance for non-distance lights */
3501                                         if( !(light->flags & LIGHT_ATTEN_DISTANCE) )
3502                                                 light->envelope = MAX_WORLD_COORD * 8.0f;
3503                                         
3504                                         /* solve distance for linear lights */
3505                                         else if( (light->flags & LIGHT_ATTEN_LINEAR ) )
3506                                                 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;
3507                                                 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;
3508
3509                                                 /*
3510                                                 add = angle * light->photons * linearScale - (dist * light->fade);
3511                                                 T = (light->photons * linearScale) - (dist * light->fade);
3512                                                 T + (dist * light->fade) = (light->photons * linearScale);
3513                                                 dist * light->fade = (light->photons * linearScale) - T;
3514                                                 dist = ((light->photons * linearScale) - T) / light->fade;
3515                                                 */
3516                                         
3517                                         /* solve for inverse square falloff */
3518                                         else
3519                                                 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3520                                                 
3521                                                 /*
3522                                                 add = light->photons / (dist * dist);
3523                                                 T = light->photons / (dist * dist);
3524                                                 T * (dist * dist) = light->photons;
3525                                                 dist = sqrt( light->photons / T );
3526                                                 */
3527                                 }
3528                                 
3529                                 /* chop radius against pvs */
3530                                 {
3531                                         /* clear bounds */
3532                                         ClearBounds( mins, maxs );
3533                                         
3534                                         /* check all leaves */
3535                                         for( i = 0; i < numBSPLeafs; i++ )
3536                                         {
3537                                                 /* get test leaf */
3538                                                 leaf = &bspLeafs[ i ];
3539                                                 
3540                                                 /* in pvs? */
3541                                                 if( leaf->cluster < 0 )
3542                                                         continue;
3543                                                 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3544                                                         continue;
3545                                                 
3546                                                 /* add this leafs bbox to the bounds */
3547                                                 VectorCopy( leaf->mins, origin );
3548                                                 AddPointToBounds( origin, mins, maxs );
3549                                                 VectorCopy( leaf->maxs, origin );
3550                                                 AddPointToBounds( origin, mins, maxs );
3551                                         }
3552                                         
3553                                         /* test to see if bounds encompass light */
3554                                         for( i = 0; i < 3; i++ )
3555                                         {
3556                                                 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )
3557                                                 {
3558                                                         //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3559                                                         //%     mins[ 0 ], mins[ 1 ], mins[ 2 ],
3560                                                         //%     maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3561                                                         //%     numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3562                                                         AddPointToBounds( light->origin, mins, maxs );
3563                                                 }
3564                                         }
3565                                         
3566                                         /* chop the bounds by a plane for area lights and spotlights */
3567                                         if( light->type == EMIT_AREA || light->type == EMIT_SPOT )
3568                                                 ChopBounds( mins, maxs, light->origin, light->normal );
3569                                         
3570                                         /* copy bounds */
3571                                         VectorCopy( mins, light->mins );
3572                                         VectorCopy( maxs, light->maxs );
3573                                         
3574                                         /* reflect bounds around light origin */
3575                                         //%     VectorMA( light->origin, -1.0f, origin, origin );
3576                                         VectorScale( light->origin, 2, origin );
3577                                         VectorSubtract( origin, maxs, origin );
3578                                         AddPointToBounds( origin, mins, maxs );
3579                                         //%     VectorMA( light->origin, -1.0f, mins, origin );
3580                                         VectorScale( light->origin, 2, origin );
3581                                         VectorSubtract( origin, mins, origin );
3582                                         AddPointToBounds( origin, mins, maxs );
3583                                          
3584                                         /* calculate spherical bounds */
3585                                         VectorSubtract( maxs, light->origin, dir );
3586                                         radius = (float) VectorLength( dir );
3587                                         
3588                                         /* if this radius is smaller than the envelope, then set the envelope to it */
3589                                         if( radius < light->envelope )
3590                                         {
3591                                                 light->envelope = radius;
3592                                                 //%     Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3593                                         }
3594                                         //%     else
3595                                         //%             Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3596                                 }
3597                                 
3598                                 /* add grid/surface only check */
3599                                 if( forGrid )
3600                                 {
3601                                         if( !(light->flags & LIGHT_GRID) )
3602                                                 light->envelope = 0.0f;
3603                                 }
3604                                 else
3605                                 {
3606                                         if( !(light->flags & LIGHT_SURFACES) )
3607                                                 light->envelope = 0.0f;
3608                                 }
3609                         }
3610                         
3611                         /* culled? */
3612                         if( light->cluster < 0 || light->envelope <= 0.0f )
3613                         {
3614                                 /* debug code */
3615                                 //%     Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3616                                 
3617                                 /* delete the light */
3618                                 numCulledLights++;
3619                                 *owner = light->next;
3620                                 if( light->w != NULL )
3621                                         free( light->w );
3622                                 free( light );
3623                                 continue;
3624                         }
3625                 }
3626                 
3627                 /* square envelope */
3628                 light->envelope2 = (light->envelope * light->envelope);
3629                 
3630                 /* increment light count */
3631                 numLights++;
3632                 
3633                 /* set next light */
3634                 owner = &((**owner).next);
3635         }
3636         
3637         /* bucket sort lights by style */
3638         memset( buckets, 0, sizeof( buckets ) );
3639         light2 = NULL;
3640         for( light = lights; light != NULL; light = light2 )
3641         {
3642                 /* get next light */
3643                 light2 = light->next;
3644                 
3645                 /* filter into correct bucket */
3646                 light->next = buckets[ light->style ];
3647                 buckets[ light->style ] = light;
3648                 
3649                 /* if any styled light is present, automatically set nocollapse */
3650                 if( light->style != LS_NORMAL )
3651                         noCollapse = qtrue;
3652         }
3653         
3654         /* filter back into light list */
3655         lights = NULL;
3656         for( i = 255; i >= 0; i-- )
3657         {
3658                 light2 = NULL;
3659                 for( light = buckets[ i ]; light != NULL; light = light2 )
3660                 {
3661                         light2 = light->next;
3662                         light->next = lights;
3663                         lights = light;
3664                 }
3665         }
3666         
3667         /* emit some statistics */
3668         Sys_Printf( "%9d total lights\n", numLights );
3669         Sys_Printf( "%9d culled lights\n", numCulledLights );
3670 }
3671
3672
3673
3674 /*
3675 CreateTraceLightsForBounds()
3676 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3677 */
3678
3679 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )
3680 {
3681         int                     i;
3682         light_t         *light;
3683         vec3_t          origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3684         float           radius, dist, length;
3685         
3686         
3687         /* potential pre-setup  */
3688         if( numLights == 0 )
3689                 SetupEnvelopes( qfalse, fast );
3690         
3691         /* debug code */
3692         //% Sys_Printf( "CTWLFB: (%4.1f %4.1f %4.1f) (%4.1f %4.1f %4.1f)\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );
3693         
3694         /* allocate the light list */
3695         trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );
3696         trace->numLights = 0;
3697         
3698         /* calculate spherical bounds */
3699         VectorAdd( mins, maxs, origin );
3700         VectorScale( origin, 0.5f, origin );
3701         VectorSubtract( maxs, origin, dir );
3702         radius = (float) VectorLength( dir );
3703         
3704         /* get length of normal vector */
3705         if( normal != NULL )
3706                 length = VectorLength( normal );
3707         else
3708         {
3709                 normal = nullVector;
3710                 length = 0;
3711         }
3712         
3713         /* test each light and see if it reaches the sphere */
3714         /* note: the attenuation code MUST match LightingAtSample() */
3715         for( light = lights; light; light = light->next )
3716         {
3717                 /* check zero sized envelope */
3718                 if( light->envelope <= 0 )
3719                 {
3720                         lightsEnvelopeCulled++;
3721                         continue;
3722                 }
3723                 
3724                 /* check flags */
3725                 if( !(light->flags & flags) )
3726                         continue;
3727                 
3728                 /* sunlight skips all this nonsense */
3729                 if( light->type != EMIT_SUN )
3730                 {
3731                         /* sun only? */
3732                         if( sunOnly )
3733                                 continue;
3734                         
3735                         /* check against pvs cluster */
3736                         if( numClusters > 0 && clusters != NULL )
3737                         {
3738                                 for( i = 0; i < numClusters; i++ )
3739                                 {
3740                                         if( ClusterVisible( light->cluster, clusters[ i ] ) )
3741                                                 break;
3742                                 }
3743                                 
3744                                 /* fixme! */
3745                                 if( i == numClusters )
3746                                 {
3747                                         lightsClusterCulled++;
3748                                         continue;
3749                                 }
3750                         }
3751                         
3752                         /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3753                         VectorSubtract( light->origin, origin, dir );
3754                         dist = VectorLength( dir );
3755                         dist -= light->envelope;
3756                         dist -= radius;
3757                         if( dist > 0 )
3758                         {
3759                                 lightsEnvelopeCulled++;
3760                                 continue;
3761                         }
3762                         
3763                         /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3764                         #if 0
3765                         skip = qfalse;
3766                         for( i = 0; i < 3; i++ )
3767                         {
3768                                 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )
3769                                         skip = qtrue;
3770                         }
3771                         if( skip )
3772                         {
3773                                 lightsBoundsCulled++;
3774                                 continue;
3775                         }
3776                         #endif
3777                 }
3778                 
3779                 /* planar surfaces (except twosided surfaces) have a couple more checks */
3780                 if( length > 0.0f && trace->twoSided == qfalse )
3781                 {
3782                         /* lights coplanar with a surface won't light it */
3783                         if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )
3784                         {
3785                                 lightsPlaneCulled++;
3786                                 continue;
3787                         }
3788                         
3789                         /* check to see if light is behind the plane */
3790                         if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )
3791                         {
3792                                 lightsPlaneCulled++;
3793                                 continue;
3794                         }
3795                 }
3796                 
3797                 /* add this light */
3798                 trace->lights[ trace->numLights++ ] = light;
3799         }
3800         
3801         /* make last night null */
3802         trace->lights[ trace->numLights ] = NULL;
3803 }
3804
3805
3806
3807 void FreeTraceLights( trace_t *trace )
3808 {
3809         if( trace->lights != NULL )
3810                 free( trace->lights );
3811 }
3812
3813
3814
3815 /*
3816 CreateTraceLightsForSurface()
3817 creates a list of lights that can potentially affect a drawsurface
3818 */
3819
3820 void CreateTraceLightsForSurface( int num, trace_t *trace )
3821 {
3822         int                                     i;
3823         vec3_t                          mins, maxs, normal;
3824         bspDrawVert_t           *dv;
3825         bspDrawSurface_t        *ds;
3826         surfaceInfo_t           *info;
3827         
3828         
3829         /* dummy check */
3830         if( num < 0 )
3831                 return;
3832         
3833         /* get drawsurface and info */
3834         ds = &bspDrawSurfaces[ num ];
3835         info = &surfaceInfos[ num ];
3836         
3837         /* get the mins/maxs for the dsurf */
3838         ClearBounds( mins, maxs );
3839         VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
3840         for( i = 0; i < ds->numVerts; i++ )
3841         {
3842                 dv = &yDrawVerts[ ds->firstVert + i ];
3843                 AddPointToBounds( dv->xyz, mins, maxs );
3844                 if( !VectorCompare( dv->normal, normal ) )
3845                         VectorClear( normal );
3846         }
3847         
3848         /* create the lights for the bounding box */
3849         CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
3850 }
3851
3852 /////////////////////////////////////////////////////////////
3853
3854 #define FLOODLIGHT_CONE_ANGLE                   88      /* degrees */
3855 #define FLOODLIGHT_NUM_ANGLE_STEPS              16
3856 #define FLOODLIGHT_NUM_ELEVATION_STEPS  4
3857 #define FLOODLIGHT_NUM_VECTORS                  (FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS)
3858
3859 static vec3_t   floodVectors[ FLOODLIGHT_NUM_VECTORS ];
3860 static int              numFloodVectors = 0;
3861
3862 void SetupFloodLight( void )
3863 {
3864         int             i, j;
3865         float   angle, elevation, angleStep, elevationStep;
3866         const char      *value;
3867         double v1,v2,v3,v4,v5;
3868
3869         /* note it */
3870         Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
3871
3872         /* calculate angular steps */
3873         angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
3874         elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
3875
3876         /* iterate angle */
3877         angle = 0.0f;
3878         for( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
3879         {
3880                 /* iterate elevation */
3881                 for( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
3882                 {
3883                         floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
3884                         floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
3885                         floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
3886                         numFloodVectors++;
3887                 }
3888         }
3889
3890         /* emit some statistics */
3891         Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
3892
3893       /* floodlight */
3894         value = ValueForKey( &entities[ 0 ], "_floodlight" );
3895
3896         if( value[ 0 ] != '\0' )
3897         {
3898                 v1=v2=v3=0;
3899                 v4=floodlightDistance;
3900                 v5=floodlightIntensity;
3901
3902                 sscanf( value, "%lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5);
3903
3904                 floodlightRGB[0]=v1;
3905                 floodlightRGB[1]=v2;
3906                 floodlightRGB[2]=v3;
3907
3908                 if (VectorLength(floodlightRGB)==0)
3909                 {
3910                         VectorSet(floodlightRGB,240,240,255);
3911                 }
3912
3913                 if (v4<1) v4=1024;
3914                 if (v5<1) v5=128;
3915
3916                 floodlightDistance=v4;
3917                 floodlightIntensity=v5;
3918
3919                 floodlighty = qtrue;
3920                 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3921         }
3922         else
3923         {
3924                 VectorSet(floodlightRGB,240,240,255);
3925                 //floodlighty = qtrue;
3926                 //Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
3927         }
3928         VectorNormalize(floodlightRGB,floodlightRGB);
3929 }
3930
3931 /*
3932 FloodLightForSample()
3933 calculates floodlight value for a given sample
3934 once again, kudos to the dirtmapping coder
3935 */
3936
3937 float FloodLightForSample( trace_t *trace , float floodLightDistance, qboolean floodLightLowQuality)
3938 {
3939         int             i;
3940         float   d;
3941         float   contribution;
3942         int     sub = 0;
3943         float   gatherLight, outLight;
3944         vec3_t  normal, worldUp, myUp, myRt, direction, displacement;
3945         float   dd;
3946         int     vecs = 0;
3947  
3948         gatherLight=0;
3949         /* dummy check */
3950         //if( !dirty )
3951         //      return 1.0f;
3952         if( trace == NULL || trace->cluster < 0 )
3953                 return 0.0f;
3954         
3955
3956         /* setup */
3957         dd = floodLightDistance;
3958         VectorCopy( trace->normal, normal );
3959         
3960         /* check if the normal is aligned to the world-up */
3961         if( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) )
3962         {
3963                 if( normal[ 2 ] == 1.0f )               
3964                 {
3965                         VectorSet( myRt, 1.0f, 0.0f, 0.0f );
3966                         VectorSet( myUp, 0.0f, 1.0f, 0.0f );
3967                 }
3968                 else if( normal[ 2 ] == -1.0f )
3969                 {
3970                         VectorSet( myRt, -1.0f, 0.0f, 0.0f );
3971                         VectorSet( myUp,  0.0f, 1.0f, 0.0f );
3972                 }
3973         }
3974         else
3975         {
3976                 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
3977                 CrossProduct( normal, worldUp, myRt );
3978                 VectorNormalize( myRt, myRt );
3979                 CrossProduct( myRt, normal, myUp );
3980                 VectorNormalize( myUp, myUp );
3981         }
3982
3983         /* vortex: optimise floodLightLowQuality a bit */
3984         if ( floodLightLowQuality == qtrue )
3985     {
3986                 /* iterate through ordered vectors */
3987                 for( i = 0; i < numFloodVectors; i++ )
3988                         if (rand()%10 != 0 ) continue;
3989         }
3990         else
3991         {
3992                 /* iterate through ordered vectors */
3993                 for( i = 0; i < numFloodVectors; i++ )
3994                 {
3995                         vecs++;
3996                  
3997                         /* transform vector into tangent space */
3998                         direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
3999                         direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4000                         direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4001
4002                         /* set endpoint */
4003                         VectorMA( trace->origin, dd, direction, trace->end );
4004
4005                         //VectorMA( trace->origin, 1, direction, trace->origin );
4006                                 
4007                         SetupTrace( trace );
4008                         /* trace */
4009                         TraceLine( trace );
4010                         contribution=1;
4011
4012                         if (trace->compileFlags & C_SKY )
4013                         {
4014                                 contribution=1.0f;
4015                         }
4016                         else if ( trace->opaque )
4017                         {
4018                                 VectorSubtract( trace->hit, trace->origin, displacement );
4019                                 d=VectorLength( displacement );
4020
4021                                 // d=trace->distance;            
4022                                 //if (d>256) gatherDirt+=1;
4023                                 contribution=d/dd;
4024                                 if (contribution>1) contribution=1.0f; 
4025                      
4026                                 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4027                         }
4028                  
4029                         gatherLight+=contribution;
4030                 }
4031         }
4032    
4033         /* early out */
4034         if( gatherLight <= 0.0f )
4035                 return 0.0f;
4036         
4037         sub=vecs;
4038
4039         if (sub<1) sub=1;
4040         gatherLight/=(sub);
4041
4042         outLight=gatherLight;
4043         if( outLight > 1.0f )
4044                 outLight = 1.0f;
4045         
4046         /* return to sender */
4047         return outLight;
4048 }
4049
4050 /*
4051 FloodLightRawLightmap
4052 lighttracer style ambient occlusion light hack.
4053 Kudos to the dirtmapping author for most of this source.
4054 VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4055 VorteX: fixed problems with deluxemapping
4056 */
4057
4058 // floodlight pass on a lightmap
4059 void FloodLightRawLightmapPass( rawLightmap_t *lm , vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale)
4060 {
4061         int                                     i, x, y, *cluster;
4062         float                           *origin, *normal, *floodlight, floodLightAmount;
4063         surfaceInfo_t           *info;
4064         trace_t                         trace;
4065         // int sx, sy;
4066         // float samples, average, *floodlight2;
4067         
4068         memset(&trace,0,sizeof(trace_t));
4069
4070         /* setup trace */
4071         trace.testOcclusion = qtrue;
4072         trace.forceSunlight = qfalse;
4073         trace.twoSided = qtrue;
4074         trace.recvShadows = lm->recvShadows;
4075         trace.numSurfaces = lm->numLightSurfaces;
4076         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4077         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4078         trace.testAll = qfalse;
4079         trace.distance = 1024;
4080         
4081         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4082         //trace.twoSided = qfalse;
4083         for( i = 0; i < trace.numSurfaces; i++ )
4084         {
4085                 /* get surface */
4086                 info = &surfaceInfos[ trace.surfaces[ i ] ];
4087                 
4088                 /* check twosidedness */
4089                 if( info->si->twoSided )
4090                 {
4091                         trace.twoSided = qtrue;
4092                         break;
4093                 }
4094         }
4095         
4096         /* gather floodlight */
4097         for( y = 0; y < lm->sh; y++ )
4098         {
4099                 for( x = 0; x < lm->sw; x++ )
4100                 {
4101                         /* get luxel */
4102                         cluster = SUPER_CLUSTER( x, y );
4103                         origin = SUPER_ORIGIN( x, y );
4104                         normal = SUPER_NORMAL( x, y );
4105                         floodlight = SUPER_FLOODLIGHT( x, y );
4106                         
4107                         /* set default dirt */
4108                         *floodlight = 0.0f;
4109                         
4110                         /* only look at mapped luxels */
4111                         if( *cluster < 0 )
4112                                 continue;
4113                         
4114                         /* copy to trace */
4115                         trace.cluster = *cluster;
4116                         VectorCopy( origin, trace.origin );
4117                         VectorCopy( normal, trace.normal );
4118    
4119                         /* get floodlight */
4120                         floodLightAmount = FloodLightForSample( &trace , lmFloodLightDistance, lmFloodLightLowQuality)*lmFloodLightIntensity;
4121                         
4122                         /* add floodlight */
4123                         floodlight[0] += lmFloodLightRGB[0]*floodLightAmount;
4124                         floodlight[1] += lmFloodLightRGB[1]*floodLightAmount;
4125                         floodlight[2] += lmFloodLightRGB[2]*floodLightAmount;
4126                         floodlight[3] += floodlightDirectionScale;
4127                 }
4128         }
4129         
4130         /* testing no filtering */
4131         return;
4132
4133 #if 0
4134         
4135         /* filter "dirt" */
4136         for( y = 0; y < lm->sh; y++ )
4137         {
4138                 for( x = 0; x < lm->sw; x++ )
4139                 {
4140                         /* get luxel */
4141                         cluster = SUPER_CLUSTER( x, y );
4142                         floodlight = SUPER_FLOODLIGHT(x, y );
4143                         
4144                         /* filter dirt by adjacency to unmapped luxels */
4145                         average = *floodlight;
4146                         samples = 1.0f;
4147                         for( sy = (y - 1); sy <= (y + 1); sy++ )
4148                         {
4149                                 if( sy < 0 || sy >= lm->sh )
4150                                         continue;
4151                                 
4152                                 for( sx = (x - 1); sx <= (x + 1); sx++ )
4153                                 {
4154                                         if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )
4155                                                 continue;
4156                                         
4157                                         /* get neighboring luxel */
4158                                         cluster = SUPER_CLUSTER( sx, sy );
4159                                         floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4160                                         if( *cluster < 0 || *floodlight2 <= 0.0f )
4161                                                 continue;
4162                                         
4163                                         /* add it */
4164                                         average += *floodlight2;
4165                                         samples += 1.0f;
4166                                 }
4167                                 
4168                                 /* bail */
4169                                 if( samples <= 0.0f )
4170                                         break;
4171                         }
4172                         
4173                         /* bail */
4174                         if( samples <= 0.0f )
4175                                 continue;
4176                         
4177                         /* scale dirt */
4178                         *floodlight = average / samples;
4179                 }
4180         }
4181 #endif
4182 }
4183
4184 void FloodLightRawLightmap( int rawLightmapNum )
4185 {
4186         rawLightmap_t           *lm;
4187
4188         /* bail if this number exceeds the number of raw lightmaps */
4189         if( rawLightmapNum >= numRawLightmaps )
4190                 return;
4191         /* get lightmap */
4192         lm = &rawLightmaps[ rawLightmapNum ];
4193
4194         /* global pass */
4195         if (floodlighty && floodlightIntensity)
4196                 FloodLightRawLightmapPass(lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, 1.0f);
4197
4198         /* custom pass */
4199         if (lm->floodlightIntensity)
4200         {
4201                 FloodLightRawLightmapPass(lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale);
4202                 numSurfacesFloodlighten += 1;
4203         }
4204 }
4205
4206 void FloodlightRawLightmaps()
4207 {
4208         Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4209         numSurfacesFloodlighten = 0;
4210         RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4211         Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4212 }
4213
4214 /*
4215 FloodLightIlluminate()
4216 illuminate floodlight into lightmap luxels
4217 */
4218
4219 void FloodlightIlluminateLightmap( rawLightmap_t *lm )
4220 {
4221         float                           *luxel, *floodlight, *deluxel, *normal;
4222         int                                     *cluster;
4223         float                           brightness;
4224         int                                     x, y, lightmapNum;
4225
4226         /* walk lightmaps */
4227         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4228         {
4229                 /* early out */
4230                 if( lm->superLuxels[ lightmapNum ] == NULL )
4231                         continue;
4232
4233                 /* apply floodlight to each luxel */
4234                 for( y = 0; y < lm->sh; y++ )
4235                 {
4236                         for( x = 0; x < lm->sw; x++ )
4237                         {
4238                                 /* get floodlight */
4239                                 floodlight = SUPER_FLOODLIGHT( x, y );
4240                                 if (!floodlight[0] && !floodlight[1] && !floodlight[2])
4241                                         continue;
4242                                                 
4243                                 /* get cluster */
4244                                 cluster = SUPER_CLUSTER( x, y );
4245
4246                                 /* only process mapped luxels */
4247                                 if( *cluster < 0 )
4248                                         continue;
4249
4250                                 /* get particulars */
4251                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
4252                                 deluxel = SUPER_DELUXEL( x, y );
4253
4254                                 /* add to lightmap */
4255                                 luxel[0]+=floodlight[0];
4256                                 luxel[1]+=floodlight[1];
4257                                 luxel[2]+=floodlight[2];
4258
4259                                 if (luxel[3]==0) luxel[3]=1;
4260
4261                                 /* add to deluxemap */
4262                                 if (deluxemap && floodlight[3] > 0)
4263                                 {
4264                                         vec3_t                          lightvector;
4265
4266                                         normal = SUPER_NORMAL( x, y );
4267                                         brightness = RGBTOGRAY( floodlight ) * ( 1.0f/255.0f ) * floodlight[3];
4268
4269                                         // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4270                                         if(brightness < 0.00390625f)
4271                                                 brightness = 0.00390625f;
4272
4273                                         VectorScale( normal, brightness, lightvector );
4274                                         VectorAdd( deluxel, lightvector, deluxel );
4275                                 }
4276                         }
4277                 }
4278         }
4279 }