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