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