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