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