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