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