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