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