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