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