]> git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/light_ydnar.c
b173b6641806ae2b707f1a72d6cde9dda897a1a7
[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 = NULL, *origin2 = NULL, *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         if (origin && origin2) {
1748                 VectorSubtract(origin2, origin, originVecs[0]);
1749         }
1750         //%     VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1751
1752         origin = origin2 = NULL;
1753
1754         /* calulate y vector */
1755         if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1756                 cluster = SUPER_CLUSTER( x, y );
1757                 origin = SUPER_ORIGIN( x, y );
1758                 //%     normal = SUPER_NORMAL( x, y );
1759                 cluster2 = SUPER_CLUSTER( x, y + 1 );
1760                 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1761                 //%     normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1762         }
1763         else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1764                 cluster = SUPER_CLUSTER( x, y - 1 );
1765                 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1766                 //%     normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1767                 cluster2 = SUPER_CLUSTER( x, y );
1768                 origin2 = SUPER_ORIGIN( x, y );
1769                 //%     normal2 = SUPER_NORMAL( x, y );
1770         }
1771         else {
1772                 Sys_FPrintf( SYS_WRN, "WARNING: Spurious lightmap T vector\n" );
1773         }
1774
1775         if (origin && origin2) {
1776                 VectorSubtract(origin2, origin, originVecs[1]);
1777         }
1778
1779         /* calculate new origin */
1780         for ( i = 0; i < 3; i++ )
1781                 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1782
1783         /* get cluster */
1784         *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1785         if ( *sampleCluster < 0 ) {
1786                 return qfalse;
1787         }
1788
1789         /* calculate new normal */
1790         normal = SUPER_NORMAL( x, y );
1791         VectorCopy( normal, sampleNormal );
1792
1793         /* return ok */
1794         return qtrue;
1795 }
1796
1797
1798 /*
1799    SubsampleRawLuxel_r()
1800    recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1801  */
1802
1803 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1804         int b, samples, mapped, lighted;
1805         int cluster[ 4 ];
1806         vec4_t luxel[ 4 ];
1807         vec3_t deluxel[ 3 ];
1808         vec3_t origin[ 4 ], normal[ 4 ];
1809         float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1810         vec3_t color, direction = { 0, 0, 0 }, total;
1811
1812
1813         /* limit check */
1814         if ( lightLuxel[ 3 ] >= lightSamples ) {
1815                 return;
1816         }
1817
1818         /* setup */
1819         VectorClear( total );
1820         mapped = 0;
1821         lighted = 0;
1822
1823         /* make 2x2 subsample stamp */
1824         for ( b = 0; b < 4; b++ )
1825         {
1826                 /* set origin */
1827                 VectorCopy( sampleOrigin, origin[ b ] );
1828
1829                 /* calculate position */
1830                 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1831                         cluster[ b ] = -1;
1832                         continue;
1833                 }
1834                 mapped++;
1835
1836                 /* increment sample count */
1837                 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1838
1839                 /* setup trace */
1840                 trace->cluster = *cluster;
1841                 VectorCopy( origin[ b ], trace->origin );
1842                 VectorCopy( normal[ b ], trace->normal );
1843
1844                 /* sample light */
1845
1846                 LightContributionToSample( trace );
1847                 if ( trace->forceSubsampling > 1.0f ) {
1848                         /* alphashadow: we subsample as deep as we can */
1849                         ++lighted;
1850                         ++mapped;
1851                         ++mapped;
1852                 }
1853
1854                 /* add to totals (fixme: make contrast function) */
1855                 VectorCopy( trace->color, luxel[ b ] );
1856                 if ( lightDeluxel ) {
1857                         VectorCopy( trace->directionContribution, deluxel[ b ] );
1858                 }
1859                 VectorAdd( total, trace->color, total );
1860                 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1861                         lighted++;
1862                 }
1863         }
1864
1865         /* subsample further? */
1866         if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1867                  ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1868                  lighted != 0 && lighted != mapped ) {
1869                 for ( b = 0; b < 4; b++ )
1870                 {
1871                         if ( cluster[ b ] < 0 ) {
1872                                 continue;
1873                         }
1874                         SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.5f ), luxel[ b ], lightDeluxel ? deluxel[ b ] : NULL );
1875                 }
1876         }
1877
1878         /* average */
1879         //%     VectorClear( color );
1880         //%     samples = 0;
1881         VectorCopy( lightLuxel, color );
1882         if ( lightDeluxel ) {
1883                 VectorCopy( lightDeluxel, direction );
1884         }
1885         samples = 1;
1886         for ( b = 0; b < 4; b++ )
1887         {
1888                 if ( cluster[ b ] < 0 ) {
1889                         continue;
1890                 }
1891                 VectorAdd( color, luxel[ b ], color );
1892                 if ( lightDeluxel ) {
1893                         VectorAdd( direction, deluxel[ b ], direction );
1894                 }
1895                 samples++;
1896         }
1897
1898         /* add to luxel */
1899         if ( samples > 0 ) {
1900                 /* average */
1901                 color[ 0 ] /= samples;
1902                 color[ 1 ] /= samples;
1903                 color[ 2 ] /= samples;
1904
1905                 /* add to color */
1906                 VectorCopy( color, lightLuxel );
1907                 lightLuxel[ 3 ] += 1.0f;
1908
1909                 if ( lightDeluxel ) {
1910                         direction[ 0 ] /= samples;
1911                         direction[ 1 ] /= samples;
1912                         direction[ 2 ] /= samples;
1913                         VectorCopy( direction, lightDeluxel );
1914                 }
1915         }
1916 }
1917
1918 /* A mostly Gaussian-like bounded random distribution (sigma is expected standard deviation) */
1919 static void GaussLikeRandom( float sigma, float *x, float *y ){
1920         float r;
1921         r = Random() * 2 * Q_PI;
1922         *x = sigma * 2.73861278752581783822 * cos( r );
1923         *y = sigma * 2.73861278752581783822 * sin( r );
1924         r = Random();
1925         r = 1 - sqrt( r );
1926         r = 1 - sqrt( r );
1927         *x *= r;
1928         *y *= r;
1929 }
1930 static void RandomSubsampleRawLuxel( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel, float *lightDeluxel ){
1931         int b, mapped;
1932         int cluster;
1933         vec3_t origin, normal;
1934         vec3_t total, totaldirection;
1935         float dx, dy;
1936
1937         VectorClear( total );
1938         VectorClear( totaldirection );
1939         mapped = 0;
1940         for ( b = 0; b < lightSamples; ++b )
1941         {
1942                 /* set origin */
1943                 VectorCopy( sampleOrigin, origin );
1944                 GaussLikeRandom( bias, &dx, &dy );
1945
1946                 /* calculate position */
1947                 if ( !SubmapRawLuxel( lm, x, y, dx, dy, &cluster, origin, normal ) ) {
1948                         cluster = -1;
1949                         continue;
1950                 }
1951                 mapped++;
1952
1953                 trace->cluster = cluster;
1954                 VectorCopy( origin, trace->origin );
1955                 VectorCopy( normal, trace->normal );
1956
1957                 LightContributionToSample( trace );
1958                 VectorAdd( total, trace->color, total );
1959                 if ( lightDeluxel ) {
1960                         VectorAdd( totaldirection, trace->directionContribution, totaldirection );
1961                 }
1962         }
1963
1964         /* add to luxel */
1965         if ( mapped > 0 ) {
1966                 /* average */
1967                 lightLuxel[ 0 ] = total[ 0 ] / mapped;
1968                 lightLuxel[ 1 ] = total[ 1 ] / mapped;
1969                 lightLuxel[ 2 ] = total[ 2 ] / mapped;
1970
1971                 if ( lightDeluxel ) {
1972                         lightDeluxel[ 0 ] = totaldirection[ 0 ] / mapped;
1973                         lightDeluxel[ 1 ] = totaldirection[ 1 ] / mapped;
1974                         lightDeluxel[ 2 ] = totaldirection[ 2 ] / mapped;
1975                 }
1976         }
1977 }
1978
1979
1980
1981 /*
1982    IlluminateRawLightmap()
1983    illuminates the luxels
1984  */
1985
1986 #define STACK_LL_SIZE           ( SUPER_LUXEL_SIZE * 64 * 64 )
1987 #define LIGHT_LUXEL( x, y )     ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1988 #define LIGHT_DELUXEL( x, y )       ( lightDeluxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_DELUXEL_SIZE ) )
1989
1990 void IlluminateRawLightmap( int rawLightmapNum ){
1991         int i, t, x, y, sx, sy, size, luxelFilterRadius, lightmapNum;
1992         int                 *cluster, *cluster2, mapped, lighted, totalLighted;
1993         size_t llSize, ldSize;
1994         rawLightmap_t       *lm;
1995         surfaceInfo_t       *info;
1996         qboolean filterColor, filterDir;
1997         float brightness;
1998         float               *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1999         unsigned char           *flag;
2000         float               *lightLuxels, *lightDeluxels, *lightLuxel, *lightDeluxel, samples, filterRadius, weight;
2001         vec3_t color, direction, averageColor, averageDir, total, temp, temp2;
2002         float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
2003         trace_t trace;
2004         float stackLightLuxels[ STACK_LL_SIZE ];
2005
2006
2007         /* bail if this number exceeds the number of raw lightmaps */
2008         if ( rawLightmapNum >= numRawLightmaps ) {
2009                 return;
2010         }
2011
2012         /* get lightmap */
2013         lm = &rawLightmaps[ rawLightmapNum ];
2014
2015         /* setup trace */
2016         trace.testOcclusion = !noTrace;
2017         trace.forceSunlight = qfalse;
2018         trace.recvShadows = lm->recvShadows;
2019         trace.numSurfaces = lm->numLightSurfaces;
2020         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
2021         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2022
2023         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
2024         trace.twoSided = qfalse;
2025         for ( i = 0; i < trace.numSurfaces; i++ )
2026         {
2027                 /* get surface */
2028                 info = &surfaceInfos[ trace.surfaces[ i ] ];
2029
2030                 /* check twosidedness */
2031                 if ( info->si->twoSided ) {
2032                         trace.twoSided = qtrue;
2033                         break;
2034                 }
2035         }
2036
2037         /* create a culled light list for this raw lightmap */
2038         CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
2039
2040         /* -----------------------------------------------------------------
2041            fill pass
2042            ----------------------------------------------------------------- */
2043
2044         /* set counts */
2045         numLuxelsIlluminated += ( lm->sw * lm->sh );
2046
2047         /* test debugging state */
2048         if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
2049                 /* debug fill the luxels */
2050                 for ( y = 0; y < lm->sh; y++ )
2051                 {
2052                         for ( x = 0; x < lm->sw; x++ )
2053                         {
2054                                 /* get cluster */
2055                                 cluster = SUPER_CLUSTER( x, y );
2056
2057                                 /* only fill mapped luxels */
2058                                 if ( *cluster < 0 ) {
2059                                         continue;
2060                                 }
2061
2062                                 /* get particulars */
2063                                 luxel = SUPER_LUXEL( 0, x, y );
2064                                 origin = SUPER_ORIGIN( x, y );
2065                                 normal = SUPER_NORMAL( x, y );
2066
2067                                 /* color the luxel with raw lightmap num? */
2068                                 if ( debugSurfaces ) {
2069                                         VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
2070                                 }
2071
2072                                 /* color the luxel with lightmap axis? */
2073                                 else if ( debugAxis ) {
2074                                         luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
2075                                         luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
2076                                         luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
2077                                 }
2078
2079                                 /* color the luxel with luxel cluster? */
2080                                 else if ( debugCluster ) {
2081                                         VectorCopy( debugColors[ *cluster % 12 ], luxel );
2082                                 }
2083
2084                                 /* color the luxel with luxel origin? */
2085                                 else if ( debugOrigin ) {
2086                                         VectorSubtract( lm->maxs, lm->mins, temp );
2087                                         VectorScale( temp, ( 1.0f / 255.0f ), temp );
2088                                         VectorSubtract( origin, lm->mins, temp2 );
2089                                         luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2090                                         luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2091                                         luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2092                                 }
2093
2094                                 /* color the luxel with the normal */
2095                                 else if ( normalmap ) {
2096                                         luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
2097                                         luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
2098                                         luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
2099                                 }
2100
2101                                 /* otherwise clear it */
2102                                 else{
2103                                         VectorClear( luxel );
2104                                 }
2105
2106                                 /* add to counts */
2107                                 luxel[ 3 ] = 1.0f;
2108                         }
2109                 }
2110         }
2111         else
2112         {
2113                 /* allocate temporary per-light luxel storage */
2114                 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2115                 ldSize = lm->sw * lm->sh * SUPER_DELUXEL_SIZE * sizeof( float );
2116                 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
2117                         lightLuxels = stackLightLuxels;
2118                 }
2119                 else{
2120                         lightLuxels = safe_malloc( llSize );
2121                 }
2122                 if ( deluxemap ) {
2123                         lightDeluxels = safe_malloc( ldSize );
2124                 }
2125                 else{
2126                         lightDeluxels = NULL;
2127                 }
2128
2129                 /* clear luxels */
2130                 //%     memset( lm->superLuxels[ 0 ], 0, llSize );
2131
2132                 /* set ambient color */
2133                 for ( y = 0; y < lm->sh; y++ )
2134                 {
2135                         for ( x = 0; x < lm->sw; x++ )
2136                         {
2137                                 /* get cluster */
2138                                 cluster = SUPER_CLUSTER( x, y );
2139                                 luxel = SUPER_LUXEL( 0, x, y );
2140                                 normal = SUPER_NORMAL( x, y );
2141                                 deluxel = SUPER_DELUXEL( x, y );
2142
2143                                 /* blacken unmapped clusters */
2144                                 if ( *cluster < 0 ) {
2145                                         VectorClear( luxel );
2146                                 }
2147
2148                                 /* set ambient */
2149                                 else
2150                                 {
2151                                         VectorCopy( ambientColor, luxel );
2152                                         if ( deluxemap ) {
2153                                                 brightness = RGBTOGRAY( ambientColor ) * ( 1.0f / 255.0f );
2154
2155                                                 // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
2156                                                 if ( brightness < 0.00390625f ) {
2157                                                         brightness = 0.00390625f;
2158                                                 }
2159
2160                                                 VectorScale( normal, brightness, deluxel );
2161                                         }
2162                                         luxel[ 3 ] = 1.0f;
2163                                 }
2164                         }
2165                 }
2166
2167                 /* clear styled lightmaps */
2168                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2169                 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2170                 {
2171                         if ( lm->superLuxels[ lightmapNum ] != NULL ) {
2172                                 memset( lm->superLuxels[ lightmapNum ], 0, size );
2173                         }
2174                 }
2175
2176                 /* debugging code */
2177                 //%     if( trace.numLights <= 0 )
2178                 //%             Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
2179
2180                 /* walk light list */
2181                 for ( i = 0; i < trace.numLights; i++ )
2182                 {
2183                         /* setup trace */
2184                         trace.light = trace.lights[ i ];
2185
2186                         /* style check */
2187                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2188                         {
2189                                 if ( lm->styles[ lightmapNum ] == trace.light->style ||
2190                                          lm->styles[ lightmapNum ] == LS_NONE ) {
2191                                         break;
2192                                 }
2193                         }
2194
2195                         /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
2196                         if ( lightmapNum >= MAX_LIGHTMAPS ) {
2197                                 Sys_FPrintf( SYS_WRN, "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
2198                                 continue;
2199                         }
2200
2201                         /* setup */
2202                         memset( lightLuxels, 0, llSize );
2203                         if ( deluxemap ) {
2204                                 memset( lightDeluxels, 0, ldSize );
2205                         }
2206                         totalLighted = 0;
2207
2208                         /* determine filter radius */
2209                         filterRadius = lm->filterRadius > trace.light->filterRadius
2210                                                    ? lm->filterRadius
2211                                                    : trace.light->filterRadius;
2212                         if ( filterRadius < 0.0f ) {
2213                                 filterRadius = 0.0f;
2214                         }
2215
2216                         /* set luxel filter radius */
2217                         luxelFilterRadius = lm->sampleSize != 0 ? superSample * filterRadius / lm->sampleSize : 0;
2218                         if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2219                                 luxelFilterRadius = 1;
2220                         }
2221
2222                         /* allocate sampling flags storage */
2223                         if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2224                                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( unsigned char );
2225                                 if ( lm->superFlags == NULL ) {
2226                                         lm->superFlags = safe_malloc( size );
2227                                 }
2228                                 memset( (void *) lm->superFlags, 0, size );
2229                         }
2230
2231                         /* initial pass, one sample per luxel */
2232                         for ( y = 0; y < lm->sh; y++ )
2233                         {
2234                                 for ( x = 0; x < lm->sw; x++ )
2235                                 {
2236                                         /* get cluster */
2237                                         cluster = SUPER_CLUSTER( x, y );
2238                                         if ( *cluster < 0 ) {
2239                                                 continue;
2240                                         }
2241
2242                                         /* get particulars */
2243                                         lightLuxel = LIGHT_LUXEL( x, y );
2244                                         lightDeluxel = LIGHT_DELUXEL( x, y );
2245                                         origin = SUPER_ORIGIN( x, y );
2246                                         normal = SUPER_NORMAL( x, y );
2247                                         flag = SUPER_FLAG( x, y );
2248
2249                                         /* set contribution count */
2250                                         lightLuxel[ 3 ] = 1.0f;
2251
2252                                         /* setup trace */
2253                                         trace.cluster = *cluster;
2254                                         VectorCopy( origin, trace.origin );
2255                                         VectorCopy( normal, trace.normal );
2256
2257                                         /* get light for this sample */
2258                                         LightContributionToSample( &trace );
2259                                         VectorCopy( trace.color, lightLuxel );
2260
2261                                         /* add the contribution to the deluxemap */
2262                                         if ( deluxemap ) {
2263                                                 VectorCopy( trace.directionContribution, lightDeluxel );
2264                                         }
2265
2266                                         /* check for evilness */
2267                                         if ( trace.forceSubsampling > 1.0f && ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2268                                                         totalLighted++;
2269                                                 *flag |= FLAG_FORCE_SUBSAMPLING; /* force */
2270                                         }
2271                                         /* add to count */
2272                                         else if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2273                                                 totalLighted++;
2274                                         }
2275                                 }
2276                         }
2277
2278                         /* don't even bother with everything else if nothing was lit */
2279                         if ( totalLighted == 0 ) {
2280                                 continue;
2281                         }
2282
2283                         /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2284                         /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2285                         if ( ( lightSamples > 1 || lightRandomSamples ) && luxelFilterRadius == 0 ) {
2286                                 /* walk luxels */
2287                                 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2288                                 {
2289                                         for ( x = 0; x < ( lm->sw - 1 ); x++ )
2290                                         {
2291                                                 /* setup */
2292                                                 mapped = 0;
2293                                                 lighted = 0;
2294                                                 VectorClear( total );
2295
2296                                                 /* test 2x2 stamp */
2297                                                 for ( t = 0; t < 4; t++ )
2298                                                 {
2299                                                         /* set sample coords */
2300                                                         sx = x + tests[ t ][ 0 ];
2301                                                         sy = y + tests[ t ][ 1 ];
2302
2303                                                         /* get cluster */
2304                                                         cluster = SUPER_CLUSTER( sx, sy );
2305                                                         if ( *cluster < 0 ) {
2306                                                                 continue;
2307                                                         }
2308                                                         mapped++;
2309
2310                                                         /* get luxel */
2311                                                         flag = SUPER_FLAG( sx, sy );
2312                                                         if ( *flag & FLAG_FORCE_SUBSAMPLING ) {
2313                                                                 /* force a lighted/mapped discrepancy so we subsample */
2314                                                                 ++lighted;
2315                                                                 ++mapped;
2316                                                                 ++mapped;
2317                                                         }
2318                                                         lightLuxel = LIGHT_LUXEL( sx, sy );
2319                                                         VectorAdd( total, lightLuxel, total );
2320                                                         if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2321                                                                 lighted++;
2322                                                         }
2323                                                 }
2324
2325                                                 /* if total color is under a certain amount, then don't bother subsampling */
2326                                                 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2327                                                         continue;
2328                                                 }
2329
2330                                                 /* if all 4 pixels are either in shadow or light, then don't subsample */
2331                                                 if ( lighted != 0 && lighted != mapped ) {
2332                                                         for ( t = 0; t < 4; t++ )
2333                                                         {
2334                                                                 /* set sample coords */
2335                                                                 sx = x + tests[ t ][ 0 ];
2336                                                                 sy = y + tests[ t ][ 1 ];
2337
2338                                                                 /* get luxel */
2339                                                                 cluster = SUPER_CLUSTER( sx, sy );
2340                                                                 if ( *cluster < 0 ) {
2341                                                                         continue;
2342                                                                 }
2343                                                                 flag = SUPER_FLAG( sx, sy );
2344                                                                 if ( *flag & FLAG_ALREADY_SUBSAMPLED ) { // already subsampled
2345                                                                         continue;
2346                                                                 }
2347                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );
2348                                                                 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2349                                                                 origin = SUPER_ORIGIN( sx, sy );
2350
2351                                                                 /* only subsample shadowed luxels */
2352                                                                 //%     if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2353                                                                 //%             continue;
2354
2355                                                                 /* subsample it */
2356                                                                 if ( lightRandomSamples ) {
2357                                                                         RandomSubsampleRawLuxel( lm, &trace, origin, sx, sy, 0.5f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2358                                                                 }
2359                                                                 else{
2360                                                                         SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f * lightSamplesSearchBoxSize, lightLuxel, deluxemap ? lightDeluxel : NULL );
2361                                                                 }
2362
2363                                                                 *flag |= FLAG_ALREADY_SUBSAMPLED;
2364
2365                                                                 /* debug code to colorize subsampled areas to yellow */
2366                                                                 //%     luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2367                                                                 //%     VectorSet( luxel, 255, 204, 0 );
2368                                                         }
2369                                                 }
2370                                         }
2371                                 }
2372                         }
2373
2374                         /* tertiary pass, apply dirt map (ambient occlusion) */
2375                         if ( 0 && dirty ) {
2376                                 /* walk luxels */
2377                                 for ( y = 0; y < lm->sh; y++ )
2378                                 {
2379                                         for ( x = 0; x < lm->sw; x++ )
2380                                         {
2381                                                 /* get cluster  */
2382                                                 cluster = SUPER_CLUSTER( x, y );
2383                                                 if ( *cluster < 0 ) {
2384                                                         continue;
2385                                                 }
2386
2387                                                 /* get particulars */
2388                                                 lightLuxel = LIGHT_LUXEL( x, y );
2389                                                 dirt = SUPER_DIRT( x, y );
2390
2391                                                 /* scale light value */
2392                                                 VectorScale( lightLuxel, *dirt, lightLuxel );
2393                                         }
2394                                 }
2395                         }
2396
2397                         /* allocate sampling lightmap storage */
2398                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2399                                 /* allocate sampling lightmap storage */
2400                                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2401                                 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2402                                 memset( lm->superLuxels[ lightmapNum ], 0, size );
2403                         }
2404
2405                         /* set style */
2406                         if ( lightmapNum > 0 ) {
2407                                 lm->styles[ lightmapNum ] = trace.light->style;
2408                                 //%     Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2409                         }
2410
2411                         /* copy to permanent luxels */
2412                         for ( y = 0; y < lm->sh; y++ )
2413                         {
2414                                 for ( x = 0; x < lm->sw; x++ )
2415                                 {
2416                                         /* get cluster and origin */
2417                                         cluster = SUPER_CLUSTER( x, y );
2418                                         if ( *cluster < 0 ) {
2419                                                 continue;
2420                                         }
2421                                         origin = SUPER_ORIGIN( x, y );
2422
2423                                         /* filter? */
2424                                         if ( luxelFilterRadius ) {
2425                                                 /* setup */
2426                                                 VectorClear( averageColor );
2427                                                 VectorClear( averageDir );
2428                                                 samples = 0.0f;
2429
2430                                                 /* cheaper distance-based filtering */
2431                                                 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2432                                                 {
2433                                                         if ( sy < 0 || sy >= lm->sh ) {
2434                                                                 continue;
2435                                                         }
2436
2437                                                         for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2438                                                         {
2439                                                                 if ( sx < 0 || sx >= lm->sw ) {
2440                                                                         continue;
2441                                                                 }
2442
2443                                                                 /* get particulars */
2444                                                                 cluster = SUPER_CLUSTER( sx, sy );
2445                                                                 if ( *cluster < 0 ) {
2446                                                                         continue;
2447                                                                 }
2448                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );
2449                                                                 lightDeluxel = LIGHT_DELUXEL( sx, sy );
2450
2451                                                                 /* create weight */
2452                                                                 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2453                                                                 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2454
2455                                                                 /* scale luxel by filter weight */
2456                                                                 VectorScale( lightLuxel, weight, color );
2457                                                                 VectorAdd( averageColor, color, averageColor );
2458                                                                 if ( deluxemap ) {
2459                                                                         VectorScale( lightDeluxel, weight, direction );
2460                                                                         VectorAdd( averageDir, direction, averageDir );
2461                                                                 }
2462                                                                 samples += weight;
2463                                                         }
2464                                                 }
2465
2466                                                 /* any samples? */
2467                                                 if ( samples <= 0.0f ) {
2468                                                         continue;
2469                                                 }
2470
2471                                                 /* scale into luxel */
2472                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2473                                                 luxel[ 3 ] = 1.0f;
2474
2475                                                 /* handle negative light */
2476                                                 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2477                                                         luxel[ 0 ] -= averageColor[ 0 ] / samples;
2478                                                         luxel[ 1 ] -= averageColor[ 1 ] / samples;
2479                                                         luxel[ 2 ] -= averageColor[ 2 ] / samples;
2480                                                 }
2481
2482                                                 /* handle normal light */
2483                                                 else
2484                                                 {
2485                                                         luxel[ 0 ] += averageColor[ 0 ] / samples;
2486                                                         luxel[ 1 ] += averageColor[ 1 ] / samples;
2487                                                         luxel[ 2 ] += averageColor[ 2 ] / samples;
2488                                                 }
2489
2490                                                 if ( deluxemap ) {
2491                                                         /* scale into luxel */
2492                                                         deluxel = SUPER_DELUXEL( x, y );
2493                                                         deluxel[ 0 ] += averageDir[ 0 ] / samples;
2494                                                         deluxel[ 1 ] += averageDir[ 1 ] / samples;
2495                                                         deluxel[ 2 ] += averageDir[ 2 ] / samples;
2496                                                 }
2497                                         }
2498
2499                                         /* single sample */
2500                                         else
2501                                         {
2502                                                 /* get particulars */
2503                                                 lightLuxel = LIGHT_LUXEL( x, y );
2504                                                 lightDeluxel = LIGHT_DELUXEL( x, y );
2505                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2506                                                 deluxel = SUPER_DELUXEL( x, y );
2507
2508                                                 /* handle negative light */
2509                                                 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2510                                                         VectorScale( averageColor, -1.0f, averageColor );
2511                                                 }
2512
2513                                                 /* add color */
2514                                                 luxel[ 3 ] = 1.0f;
2515
2516                                                 /* handle negative light */
2517                                                 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2518                                                         VectorSubtract( luxel, lightLuxel, luxel );
2519                                                 }
2520
2521                                                 /* handle normal light */
2522                                                 else{
2523                                                         VectorAdd( luxel, lightLuxel, luxel );
2524                                                 }
2525
2526                                                 if ( deluxemap ) {
2527                                                         VectorAdd( deluxel, lightDeluxel, deluxel );
2528                                                 }
2529                                         }
2530                                 }
2531                         }
2532                 }
2533
2534                 /* free temporary luxels */
2535                 if ( lightLuxels != stackLightLuxels ) {
2536                         free( lightLuxels );
2537                 }
2538
2539                 if ( deluxemap ) {
2540                         free( lightDeluxels );
2541                 }
2542         }
2543
2544         /* free light list */
2545         FreeTraceLights( &trace );
2546
2547         /* floodlight pass */
2548         if ( floodlighty ) {
2549                 FloodlightIlluminateLightmap( lm );
2550         }
2551
2552         if ( debugnormals ) {
2553                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2554                 {
2555                         /* early out */
2556                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2557                                 continue;
2558                         }
2559
2560                         for ( y = 0; y < lm->sh; y++ )
2561                         {
2562                                 for ( x = 0; x < lm->sw; x++ )
2563                                 {
2564                                         /* get cluster */
2565                                         cluster = SUPER_CLUSTER( x, y );
2566                                         //%     if( *cluster < 0 )
2567                                         //%             continue;
2568
2569                                         /* get particulars */
2570                                         luxel = SUPER_LUXEL( lightmapNum, x, y );
2571                                         normal = SUPER_NORMAL(  x, y );
2572
2573                                         luxel[0] = ( normal[0] * 127 ) + 127;
2574                                         luxel[1] = ( normal[1] * 127 ) + 127;
2575                                         luxel[2] = ( normal[2] * 127 ) + 127;
2576                                 }
2577                         }
2578                 }
2579         }
2580
2581         /*      -----------------------------------------------------------------
2582             dirt pass
2583             ----------------------------------------------------------------- */
2584
2585         if ( dirty ) {
2586                 /* walk lightmaps */
2587                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2588                 {
2589                         /* early out */
2590                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2591                                 continue;
2592                         }
2593
2594                         /* apply dirt to each luxel */
2595                         for ( y = 0; y < lm->sh; y++ )
2596                         {
2597                                 for ( x = 0; x < lm->sw; x++ )
2598                                 {
2599                                         /* get cluster */
2600                                         cluster = SUPER_CLUSTER( x, y );
2601
2602                                         /* get particulars */
2603                                         luxel = SUPER_LUXEL( lightmapNum, x, y );
2604                                         dirt = SUPER_DIRT( x, y );
2605
2606                                         /* apply dirt */
2607                                         VectorScale( luxel, *dirt, luxel );
2608
2609                                         /* debugging */
2610                                         if ( dirtDebug ) {
2611                                                 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2612                                         }
2613                                 }
2614                         }
2615                 }
2616         }
2617
2618         /* -----------------------------------------------------------------
2619            filter pass
2620            ----------------------------------------------------------------- */
2621
2622         /* walk lightmaps */
2623         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2624         {
2625                 /* early out */
2626                 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2627                         continue;
2628                 }
2629
2630                 /* average occluded luxels from neighbors */
2631                 for ( y = 0; y < lm->sh; y++ )
2632                 {
2633                         for ( x = 0; x < lm->sw; x++ )
2634                         {
2635                                 /* get particulars */
2636                                 cluster = SUPER_CLUSTER( x, y );
2637                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2638                                 deluxel = SUPER_DELUXEL( x, y );
2639                                 normal = SUPER_NORMAL( x, y );
2640
2641                                 /* determine if filtering is necessary */
2642                                 filterColor = qfalse;
2643                                 filterDir = qfalse;
2644                                 if ( *cluster < 0 ||
2645                                          ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2646                                         filterColor = qtrue;
2647                                 }
2648
2649                                 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2650                                         filterDir = qtrue;
2651                                 }
2652
2653                                 if ( !filterColor && !filterDir ) {
2654                                         continue;
2655                                 }
2656
2657                                 /* choose seed amount */
2658                                 VectorClear( averageColor );
2659                                 VectorClear( averageDir );
2660                                 samples = 0.0f;
2661
2662                                 /* walk 3x3 matrix */
2663                                 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2664                                 {
2665                                         if ( sy < 0 || sy >= lm->sh ) {
2666                                                 continue;
2667                                         }
2668
2669                                         for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2670                                         {
2671                                                 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2672                                                         continue;
2673                                                 }
2674
2675                                                 /* get neighbor's particulars */
2676                                                 cluster2 = SUPER_CLUSTER( sx, sy );
2677                                                 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2678                                                 deluxel2 = SUPER_DELUXEL( sx, sy );
2679
2680                                                 /* ignore unmapped/unlit luxels */
2681                                                 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2682                                                          ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2683                                                         continue;
2684                                                 }
2685
2686                                                 /* add its distinctiveness to our own */
2687                                                 VectorAdd( averageColor, luxel2, averageColor );
2688                                                 samples += luxel2[ 3 ];
2689                                                 if ( filterDir ) {
2690                                                         VectorAdd( averageDir, deluxel2, averageDir );
2691                                                 }
2692                                         }
2693                                 }
2694
2695                                 /* fall through */
2696                                 if ( samples <= 0.0f ) {
2697                                         continue;
2698                                 }
2699
2700                                 /* dark lightmap seams */
2701                                 if ( dark ) {
2702                                         if ( lightmapNum == 0 ) {
2703                                                 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2704                                         }
2705                                         samples += 2.0f;
2706                                 }
2707
2708                                 /* average it */
2709                                 if ( filterColor ) {
2710                                         VectorDivide( averageColor, samples, luxel );
2711                                         luxel[ 3 ] = 1.0f;
2712                                 }
2713                                 if ( filterDir ) {
2714                                         VectorDivide( averageDir, samples, deluxel );
2715                                 }
2716
2717                                 /* set cluster to -3 */
2718                                 if ( *cluster < 0 ) {
2719                                         *cluster = CLUSTER_FLOODED;
2720                                 }
2721                         }
2722                 }
2723         }
2724 }
2725
2726
2727
2728 /*
2729    IlluminateVertexes()
2730    light the surface vertexes
2731  */
2732
2733 #define VERTEX_NUDGE    4.0f
2734
2735 void IlluminateVertexes( int num ){
2736         int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2737         int lightmapNum, numAvg;
2738         float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2739         vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2740         bspDrawSurface_t    *ds;
2741         surfaceInfo_t       *info;
2742         rawLightmap_t       *lm;
2743         bspDrawVert_t       *verts;
2744         trace_t trace;
2745         float floodLightAmount;
2746         vec3_t floodColor;
2747
2748
2749         /* get surface, info, and raw lightmap */
2750         ds = &bspDrawSurfaces[ num ];
2751         info = &surfaceInfos[ num ];
2752         lm = info->lm;
2753
2754         /* -----------------------------------------------------------------
2755            illuminate the vertexes
2756            ----------------------------------------------------------------- */
2757
2758         /* calculate vertex lighting for surfaces without lightmaps */
2759         if ( lm == NULL || cpmaHack ) {
2760                 /* setup trace */
2761                 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2762                 trace.forceSunlight = info->si->forceSunlight;
2763                 trace.recvShadows = info->recvShadows;
2764                 trace.numSurfaces = 1;
2765                 trace.surfaces = &num;
2766                 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2767
2768                 /* twosided lighting */
2769                 trace.twoSided = info->si->twoSided;
2770
2771                 /* make light list for this surface */
2772                 CreateTraceLightsForSurface( num, &trace );
2773
2774                 /* setup */
2775                 verts = yDrawVerts + ds->firstVert;
2776                 numAvg = 0;
2777                 memset( avgColors, 0, sizeof( avgColors ) );
2778
2779                 /* walk the surface verts */
2780                 for ( i = 0; i < ds->numVerts; i++ )
2781                 {
2782                         /* get vertex luxel */
2783                         radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2784
2785                         /* color the luxel with raw lightmap num? */
2786                         if ( debugSurfaces ) {
2787                                 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2788                         }
2789
2790                         /* color the luxel with luxel origin? */
2791                         else if ( debugOrigin ) {
2792                                 VectorSubtract( info->maxs, info->mins, temp );
2793                                 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2794                                 VectorSubtract( origin, lm->mins, temp2 );
2795                                 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2796                                 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2797                                 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2798                         }
2799
2800                         /* color the luxel with the normal */
2801                         else if ( normalmap ) {
2802                                 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2803                                 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2804                                 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2805                         }
2806
2807                         /* illuminate the vertex */
2808                         else
2809                         {
2810                                 /* clear vertex luxel */
2811                                 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2812
2813                                 /* try at initial origin */
2814                                 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2815                                 if ( trace.cluster >= 0 ) {
2816                                         /* setup trace */
2817                                         VectorCopy( verts[ i ].xyz, trace.origin );
2818                                         VectorCopy( verts[ i ].normal, trace.normal );
2819
2820                                         /* r7 dirt */
2821                                         if ( dirty && !bouncing ) {
2822                                                 dirt = DirtForSample( &trace );
2823                                         }
2824                                         else{
2825                                                 dirt = 1.0f;
2826                                         }
2827
2828                                         /* jal: floodlight */
2829                                         floodLightAmount = 0.0f;
2830                                         VectorClear( floodColor );
2831                                         if ( floodlighty && !bouncing ) {
2832                                                 floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2833                                                 VectorScale( floodlightRGB, floodLightAmount, floodColor );
2834                                         }
2835
2836                                         /* trace */
2837                                         LightingAtSample( &trace, ds->vertexStyles, colors );
2838
2839                                         /* store */
2840                                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2841                                         {
2842                                                 /* r7 dirt */
2843                                                 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2844
2845                                                 /* jal: floodlight */
2846                                                 VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2847
2848                                                 /* store */
2849                                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2850                                                 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2851                                                 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2852                                         }
2853                                 }
2854
2855                                 /* is this sample bright enough? */
2856                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2857                                 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2858                                          radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2859                                          radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2860                                         /* nudge the sample point around a bit */
2861                                         for ( x = 0; x < 5; x++ )
2862                                         {
2863                                                 /* two's complement 0, 1, -1, 2, -2, etc */
2864                                                 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2865
2866                                                 for ( y = 0; y < 5; y++ )
2867                                                 {
2868                                                         y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2869
2870                                                         for ( z = 0; z < 5; z++ )
2871                                                         {
2872                                                                 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2873
2874                                                                 /* nudge origin */
2875                                                                 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2876                                                                 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2877                                                                 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2878
2879                                                                 /* try at nudged origin */
2880                                                                 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2881                                                                 if ( trace.cluster < 0 ) {
2882                                                                         continue;
2883                                                                 }
2884
2885                                                                 /* r7 dirt */
2886                                                                 if ( dirty && !bouncing ) {
2887                                                                         dirt = DirtForSample( &trace );
2888                                                                 }
2889                                                                 else{
2890                                                                         dirt = 1.0f;
2891                                                                 }
2892
2893                                                                 /* jal: floodlight */
2894                                                                 floodLightAmount = 0.0f;
2895                                                                 VectorClear( floodColor );
2896                                                                 if ( floodlighty && !bouncing ) {
2897                                                                         floodLightAmount = floodlightIntensity * FloodLightForSample( &trace, floodlightDistance, floodlight_lowquality );
2898                                                                         VectorScale( floodlightRGB, floodLightAmount, floodColor );
2899                                                                 }
2900
2901                                                                 /* trace */
2902                                                                 LightingAtSample( &trace, ds->vertexStyles, colors );
2903
2904                                                                 /* store */
2905                                                                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2906                                                                 {
2907                                                                         /* r7 dirt */
2908                                                                         VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2909
2910                                                                         /* jal: floodlight */
2911                                                                         VectorAdd( colors[ lightmapNum ], floodColor, colors[ lightmapNum ] );
2912
2913                                                                         /* store */
2914                                                                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2915                                                                         VectorCopy( colors[ lightmapNum ], radVertLuxel );
2916                                                                 }
2917
2918                                                                 /* bright enough? */
2919                                                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2920                                                                 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2921                                                                          radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2922                                                                          radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2923                                                                         x = y = z = 1000;
2924                                                                 }
2925                                                         }
2926                                                 }
2927                                         }
2928                                 }
2929
2930                                 /* add to average? */
2931                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2932                                 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2933                                          radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2934                                          radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2935                                         numAvg++;
2936                                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2937                                         {
2938                                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2939                                                 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2940                                         }
2941                                 }
2942                         }
2943
2944                         /* another happy customer */
2945                         numVertsIlluminated++;
2946                 }
2947
2948                 /* set average color */
2949                 if ( numAvg > 0 ) {
2950                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2951                                 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2952                 }
2953                 else
2954                 {
2955                         VectorCopy( ambientColor, avgColors[ 0 ] );
2956                 }
2957
2958                 /* clean up and store vertex color */
2959                 for ( i = 0; i < ds->numVerts; i++ )
2960                 {
2961                         /* get vertex luxel */
2962                         radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2963
2964                         /* store average in occluded vertexes */
2965                         if ( radVertLuxel[ 0 ] < 0.0f ) {
2966                                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2967                                 {
2968                                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2969                                         VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2970
2971                                         /* debug code */
2972                                         //%     VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2973                                 }
2974                         }
2975
2976                         /* store it */
2977                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2978                         {
2979                                 /* get luxels */
2980                                 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2981                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2982
2983                                 /* store */
2984                                 if ( bouncing || bounce == 0 || !bounceOnly ) {
2985                                         VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2986                                 }
2987                                 if ( !info->si->noVertexLight ) {
2988                                         ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2989                                 }
2990                         }
2991                 }
2992
2993                 /* free light list */
2994                 FreeTraceLights( &trace );
2995
2996                 /* return to sender */
2997                 return;
2998         }
2999
3000         /* -----------------------------------------------------------------
3001            reconstitute vertex lighting from the luxels
3002            ----------------------------------------------------------------- */
3003
3004         /* set styles from lightmap */
3005         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3006                 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
3007
3008         /* get max search radius */
3009         maxRadius = lm->sw;
3010         maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
3011
3012         /* walk the surface verts */
3013         verts = yDrawVerts + ds->firstVert;
3014         for ( i = 0; i < ds->numVerts; i++ )
3015         {
3016                 /* do each lightmap */
3017                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
3018                 {
3019                         /* early out */
3020                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
3021                                 continue;
3022                         }
3023
3024                         /* get luxel coords */
3025                         x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
3026                         y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
3027                         if ( x < 0 ) {
3028                                 x = 0;
3029                         }
3030                         else if ( x >= lm->sw ) {
3031                                 x = lm->sw - 1;
3032                         }
3033                         if ( y < 0 ) {
3034                                 y = 0;
3035                         }
3036                         else if ( y >= lm->sh ) {
3037                                 y = lm->sh - 1;
3038                         }
3039
3040                         /* get vertex luxels */
3041                         vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3042                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
3043
3044                         /* color the luxel with the normal? */
3045                         if ( normalmap ) {
3046                                 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
3047                                 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
3048                                 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
3049                         }
3050
3051                         /* color the luxel with surface num? */
3052                         else if ( debugSurfaces ) {
3053                                 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
3054                         }
3055
3056                         /* divine color from the superluxels */
3057                         else
3058                         {
3059                                 /* increasing radius */
3060                                 VectorClear( radVertLuxel );
3061                                 samples = 0.0f;
3062                                 for ( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )
3063                                 {
3064                                         /* sample within radius */
3065                                         for ( sy = ( y - radius ); sy <= ( y + radius ); sy++ )
3066                                         {
3067                                                 if ( sy < 0 || sy >= lm->sh ) {
3068                                                         continue;
3069                                                 }
3070
3071                                                 for ( sx = ( x - radius ); sx <= ( x + radius ); sx++ )
3072                                                 {
3073                                                         if ( sx < 0 || sx >= lm->sw ) {
3074                                                                 continue;
3075                                                         }
3076
3077                                                         /* get luxel particulars */
3078                                                         luxel = SUPER_LUXEL( lightmapNum, sx, sy );
3079                                                         cluster = SUPER_CLUSTER( sx, sy );
3080                                                         if ( *cluster < 0 ) {
3081                                                                 continue;
3082                                                         }
3083
3084                                                         /* testing: must be brigher than ambient color */
3085                                                         //%     if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )
3086                                                         //%             continue;
3087
3088                                                         /* add its distinctiveness to our own */
3089                                                         VectorAdd( radVertLuxel, luxel, radVertLuxel );
3090                                                         samples += luxel[ 3 ];
3091                                                 }
3092                                         }
3093                                 }
3094
3095                                 /* any color? */
3096                                 if ( samples > 0.0f ) {
3097                                         VectorDivide( radVertLuxel, samples, radVertLuxel );
3098                                 }
3099                                 else{
3100                                         VectorCopy( ambientColor, radVertLuxel );
3101                                 }
3102                         }
3103
3104                         /* store into floating point storage */
3105                         VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
3106                         numVertsIlluminated++;
3107
3108                         /* store into bytes (for vertex approximation) */
3109                         if ( !info->si->noVertexLight ) {
3110                                 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );
3111                         }
3112                 }
3113         }
3114 }
3115
3116
3117
3118 /* -------------------------------------------------------------------------------
3119
3120    light optimization (-fast)
3121
3122    creates a list of lights that will affect a surface and stores it in tw
3123    this is to optimize surface lighting by culling out as many of the
3124    lights in the world as possible from further calculation
3125
3126    ------------------------------------------------------------------------------- */
3127
3128 /*
3129    SetupBrushes()
3130    determines opaque brushes in the world and find sky shaders for sunlight calculations
3131  */
3132
3133 void SetupBrushesFlags( unsigned int mask_any, unsigned int test_any, unsigned int mask_all, unsigned int test_all ){
3134         int i, j, b;
3135         unsigned int compileFlags, allCompileFlags;
3136         qboolean inside;
3137         bspBrush_t      *brush;
3138         bspBrushSide_t  *side;
3139         bspShader_t     *shader;
3140         shaderInfo_t    *si;
3141
3142
3143         /* note it */
3144         Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );
3145
3146         /* allocate */
3147         if ( opaqueBrushes == NULL ) {
3148                 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );
3149         }
3150
3151         /* clear */
3152         memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );
3153         numOpaqueBrushes = 0;
3154
3155         /* walk the list of worldspawn brushes */
3156         for ( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )
3157         {
3158                 /* get brush */
3159                 b = bspModels[ 0 ].firstBSPBrush + i;
3160                 brush = &bspBrushes[ b ];
3161
3162                 /* check all sides */
3163                 inside = qtrue;
3164                 compileFlags = 0;
3165                 allCompileFlags = ~( 0u );
3166                 for ( j = 0; j < brush->numSides && inside; j++ )
3167                 {
3168                         /* do bsp shader calculations */
3169                         side = &bspBrushSides[ brush->firstSide + j ];
3170                         shader = &bspShaders[ side->shaderNum ];
3171
3172                         /* get shader info */
3173                         si = ShaderInfoForShaderNull( shader->shader );
3174                         if ( si == NULL ) {
3175                                 continue;
3176                         }
3177
3178                         /* or together compile flags */
3179                         compileFlags |= si->compileFlags;
3180                         allCompileFlags &= si->compileFlags;
3181                 }
3182
3183                 /* determine if this brush is opaque to light */
3184                 if ( ( compileFlags & mask_any ) == test_any && ( allCompileFlags & mask_all ) == test_all ) {
3185                         opaqueBrushes[ b >> 3 ] |= ( 1 << ( b & 7 ) );
3186                         numOpaqueBrushes++;
3187                         maxOpaqueBrush = i;
3188                 }
3189         }
3190
3191         /* emit some statistics */
3192         Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );
3193 }
3194 void SetupBrushes( void ){
3195         SetupBrushesFlags( C_TRANSLUCENT, 0, 0, 0 );
3196 }
3197
3198
3199
3200 /*
3201    ClusterVisible()
3202    determines if two clusters are visible to each other using the PVS
3203  */
3204
3205 qboolean ClusterVisible( int a, int b ){
3206         int leafBytes;
3207         byte        *pvs;
3208
3209
3210         /* dummy check */
3211         if ( a < 0 || b < 0 ) {
3212                 return qfalse;
3213         }
3214
3215         /* early out */
3216         if ( a == b ) {
3217                 return qtrue;
3218         }
3219
3220         /* not vised? */
3221         if ( numBSPVisBytes <= 8 ) {
3222                 return qtrue;
3223         }
3224
3225         /* get pvs data */
3226         /* portalClusters = ((int *) bspVisBytes)[ 0 ]; */
3227         leafBytes = ( (int*) bspVisBytes )[ 1 ];
3228         pvs = bspVisBytes + VIS_HEADER_SIZE + ( a * leafBytes );
3229
3230         /* check */
3231         if ( ( pvs[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3232                 return qtrue;
3233         }
3234         return qfalse;
3235 }
3236
3237
3238
3239 /*
3240    PointInLeafNum_r()
3241    borrowed from vlight.c
3242  */
3243
3244 int PointInLeafNum_r( vec3_t point, int nodenum ){
3245         int leafnum;
3246         vec_t dist;
3247         bspNode_t       *node;
3248         bspPlane_t  *plane;
3249
3250
3251         while ( nodenum >= 0 )
3252         {
3253                 node = &bspNodes[ nodenum ];
3254                 plane = &bspPlanes[ node->planeNum ];
3255                 dist = DotProduct( point, plane->normal ) - plane->dist;
3256                 if ( dist > 0.1 ) {
3257                         nodenum = node->children[ 0 ];
3258                 }
3259                 else if ( dist < -0.1 ) {
3260                         nodenum = node->children[ 1 ];
3261                 }
3262                 else
3263                 {
3264                         leafnum = PointInLeafNum_r( point, node->children[ 0 ] );
3265                         if ( bspLeafs[ leafnum ].cluster != -1 ) {
3266                                 return leafnum;
3267                         }
3268                         nodenum = node->children[ 1 ];
3269                 }
3270         }
3271
3272         leafnum = -nodenum - 1;
3273         return leafnum;
3274 }
3275
3276
3277
3278 /*
3279    PointInLeafnum()
3280    borrowed from vlight.c
3281  */
3282
3283 int PointInLeafNum( vec3_t point ){
3284         return PointInLeafNum_r( point, 0 );
3285 }
3286
3287
3288
3289 /*
3290    ClusterVisibleToPoint() - ydnar
3291    returns qtrue if point can "see" cluster
3292  */
3293
3294 qboolean ClusterVisibleToPoint( vec3_t point, int cluster ){
3295         int pointCluster;
3296
3297
3298         /* get leafNum for point */
3299         pointCluster = ClusterForPoint( point );
3300         if ( pointCluster < 0 ) {
3301                 return qfalse;
3302         }
3303
3304         /* check pvs */
3305         return ClusterVisible( pointCluster, cluster );
3306 }
3307
3308
3309
3310 /*
3311    ClusterForPoint() - ydnar
3312    returns the pvs cluster for point
3313  */
3314
3315 int ClusterForPoint( vec3_t point ){
3316         int leafNum;
3317
3318
3319         /* get leafNum for point */
3320         leafNum = PointInLeafNum( point );
3321         if ( leafNum < 0 ) {
3322                 return -1;
3323         }
3324
3325         /* return the cluster */
3326         return bspLeafs[ leafNum ].cluster;
3327 }
3328
3329
3330
3331 /*
3332    ClusterForPointExt() - ydnar
3333    also takes brushes into account for occlusion testing
3334  */
3335
3336 int ClusterForPointExt( vec3_t point, float epsilon ){
3337         int i, j, b, leafNum, cluster;
3338         float dot;
3339         qboolean inside;
3340         int             *brushes, numBSPBrushes;
3341         bspLeaf_t       *leaf;
3342         bspBrush_t      *brush;
3343         bspPlane_t      *plane;
3344
3345
3346         /* get leaf for point */
3347         leafNum = PointInLeafNum( point );
3348         if ( leafNum < 0 ) {
3349                 return -1;
3350         }
3351         leaf = &bspLeafs[ leafNum ];
3352
3353         /* get the cluster */
3354         cluster = leaf->cluster;
3355         if ( cluster < 0 ) {
3356                 return -1;
3357         }
3358
3359         /* transparent leaf, so check point against all brushes in the leaf */
3360         brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3361         numBSPBrushes = leaf->numBSPLeafBrushes;
3362         for ( i = 0; i < numBSPBrushes; i++ )
3363         {
3364                 /* get parts */
3365                 b = brushes[ i ];
3366                 if ( b > maxOpaqueBrush ) {
3367                         continue;
3368                 }
3369                 brush = &bspBrushes[ b ];
3370                 if ( !( opaqueBrushes[ b >> 3 ] & ( 1 << ( b & 7 ) ) ) ) {
3371                         continue;
3372                 }
3373
3374                 /* check point against all planes */
3375                 inside = qtrue;
3376                 for ( j = 0; j < brush->numSides && inside; j++ )
3377                 {
3378                         plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];
3379                         dot = DotProduct( point, plane->normal );
3380                         dot -= plane->dist;
3381                         if ( dot > epsilon ) {
3382                                 inside = qfalse;
3383                         }
3384                 }
3385
3386                 /* if inside, return bogus cluster */
3387                 if ( inside ) {
3388                         return -1 - b;
3389                 }
3390         }
3391
3392         /* if the point made it this far, it's not inside any opaque brushes */
3393         return cluster;
3394 }
3395
3396
3397
3398 /*
3399    ClusterForPointExtFilter() - ydnar
3400    adds cluster checking against a list of known valid clusters
3401  */
3402
3403 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters ){
3404         int i, cluster;
3405
3406
3407         /* get cluster for point */
3408         cluster = ClusterForPointExt( point, epsilon );
3409
3410         /* check if filtering is necessary */
3411         if ( cluster < 0 || numClusters <= 0 || clusters == NULL ) {
3412                 return cluster;
3413         }
3414
3415         /* filter */
3416         for ( i = 0; i < numClusters; i++ )
3417         {
3418                 if ( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) ) {
3419                         return cluster;
3420                 }
3421         }
3422
3423         /* failed */
3424         return -1;
3425 }
3426
3427
3428
3429 /*
3430    ShaderForPointInLeaf() - ydnar
3431    checks a point against all brushes in a leaf, returning the shader of the brush
3432    also sets the cumulative surface and content flags for the brush hit
3433  */
3434
3435 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags ){
3436         int i, j;
3437         float dot;
3438         qboolean inside;
3439         int             *brushes, numBSPBrushes;
3440         bspLeaf_t           *leaf;
3441         bspBrush_t      *brush;
3442         bspBrushSide_t  *side;
3443         bspPlane_t      *plane;
3444         bspShader_t     *shader;
3445         int allSurfaceFlags, allContentFlags;
3446
3447
3448         /* clear things out first */
3449         *surfaceFlags = 0;
3450         *contentFlags = 0;
3451
3452         /* get leaf */
3453         if ( leafNum < 0 ) {
3454                 return -1;
3455         }
3456         leaf = &bspLeafs[ leafNum ];
3457
3458         /* transparent leaf, so check point against all brushes in the leaf */
3459         brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];
3460         numBSPBrushes = leaf->numBSPLeafBrushes;
3461         for ( i = 0; i < numBSPBrushes; i++ )
3462         {
3463                 /* get parts */
3464                 brush = &bspBrushes[ brushes[ i ] ];
3465
3466                 /* check point against all planes */
3467                 inside = qtrue;
3468                 allSurfaceFlags = 0;
3469                 allContentFlags = 0;
3470                 for ( j = 0; j < brush->numSides && inside; j++ )
3471                 {
3472                         side = &bspBrushSides[ brush->firstSide + j ];
3473                         plane = &bspPlanes[ side->planeNum ];
3474                         dot = DotProduct( point, plane->normal );
3475                         dot -= plane->dist;
3476                         if ( dot > epsilon ) {
3477                                 inside = qfalse;
3478                         }
3479                         else
3480                         {
3481                                 shader = &bspShaders[ side->shaderNum ];
3482                                 allSurfaceFlags |= shader->surfaceFlags;
3483                                 allContentFlags |= shader->contentFlags;
3484                         }
3485                 }
3486
3487                 /* handle if inside */
3488                 if ( inside ) {
3489                         /* if there are desired flags, check for same and continue if they aren't matched */
3490                         if ( wantContentFlags && !( wantContentFlags & allContentFlags ) ) {
3491                                 continue;
3492                         }
3493                         if ( wantSurfaceFlags && !( wantSurfaceFlags & allSurfaceFlags ) ) {
3494                                 continue;
3495                         }
3496
3497                         /* store the cumulative flags and return the brush shader (which is mostly useless) */
3498                         *surfaceFlags = allSurfaceFlags;
3499                         *contentFlags = allContentFlags;
3500                         return brush->shaderNum;
3501                 }
3502         }
3503
3504         /* if the point made it this far, it's not inside any brushes */
3505         return -1;
3506 }
3507
3508
3509
3510 /*
3511    ChopBounds()
3512    chops a bounding box by the plane defined by origin and normal
3513    returns qfalse if the bounds is entirely clipped away
3514
3515    this is not exactly the fastest way to do this...
3516  */
3517
3518 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal ){
3519         /* FIXME: rewrite this so it doesn't use bloody brushes */
3520         return qtrue;
3521 }
3522
3523
3524
3525 /*
3526    SetupEnvelopes()
3527    calculates each light's effective envelope,
3528    taking into account brightness, type, and pvs.
3529  */
3530
3531 #define LIGHT_EPSILON   0.125f
3532 #define LIGHT_NUDGE     2.0f
3533
3534 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag ){
3535         int i, x, y, z, x1, y1, z1;
3536         light_t     *light, *light2, **owner;
3537         bspLeaf_t   *leaf;
3538         vec3_t origin, dir, mins, maxs;
3539         float radius, intensity;
3540         light_t     *buckets[ 256 ];
3541
3542
3543         /* early out for weird cases where there are no lights */
3544         if ( lights == NULL ) {
3545                 return;
3546         }
3547
3548         /* note it */
3549         Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );
3550
3551         /* count lights */
3552         numLights = 0;
3553         numCulledLights = 0;
3554         owner = &lights;
3555         while ( *owner != NULL )
3556         {
3557                 /* get light */
3558                 light = *owner;
3559
3560                 /* handle negative lights */
3561                 if ( light->photons < 0.0f || light->add < 0.0f ) {
3562                         light->photons *= -1.0f;
3563                         light->add *= -1.0f;
3564                         light->flags |= LIGHT_NEGATIVE;
3565                 }
3566
3567                 /* sunlight? */
3568                 if ( light->type == EMIT_SUN ) {
3569                         /* special cased */
3570                         light->cluster = 0;
3571                         light->envelope = MAX_WORLD_COORD * 8.0f;
3572                         VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );
3573                         VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );
3574                 }
3575
3576                 /* everything else */
3577                 else
3578                 {
3579                         /* get pvs cluster for light */
3580                         light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );
3581
3582                         /* invalid cluster? */
3583                         if ( light->cluster < 0 ) {
3584                                 /* nudge the sample point around a bit */
3585                                 for ( x = 0; x < 4; x++ )
3586                                 {
3587                                         /* two's complement 0, 1, -1, 2, -2, etc */
3588                                         x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
3589
3590                                         for ( y = 0; y < 4; y++ )
3591                                         {
3592                                                 y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
3593
3594                                                 for ( z = 0; z < 4; z++ )
3595                                                 {
3596                                                         z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
3597
3598                                                         /* nudge origin */
3599                                                         origin[ 0 ] = light->origin[ 0 ] + ( LIGHT_NUDGE * x1 );
3600                                                         origin[ 1 ] = light->origin[ 1 ] + ( LIGHT_NUDGE * y1 );
3601                                                         origin[ 2 ] = light->origin[ 2 ] + ( LIGHT_NUDGE * z1 );
3602
3603                                                         /* try at nudged origin */
3604                                                         light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );
3605                                                         if ( light->cluster < 0 ) {
3606                                                                 continue;
3607                                                         }
3608
3609                                                         /* set origin */
3610                                                         VectorCopy( origin, light->origin );
3611                                                 }
3612                                         }
3613                                 }
3614                         }
3615
3616                         /* only calculate for lights in pvs and outside of opaque brushes */
3617                         if ( light->cluster >= 0 ) {
3618                                 /* set light fast flag */
3619                                 if ( fastFlag ) {
3620                                         light->flags |= LIGHT_FAST_TEMP;
3621                                 }
3622                                 else{
3623                                         light->flags &= ~LIGHT_FAST_TEMP;
3624                                 }
3625                                 if ( fastpoint && ( light->type != EMIT_AREA ) ) {
3626                                         light->flags |= LIGHT_FAST_TEMP;
3627                                 }
3628                                 if ( light->si && light->si->noFast ) {
3629                                         light->flags &= ~( LIGHT_FAST | LIGHT_FAST_TEMP );
3630                                 }
3631
3632                                 /* clear light envelope */
3633                                 light->envelope = 0;
3634
3635                                 /* handle area lights */
3636                                 if ( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL ) {
3637                                         light->envelope = MAX_WORLD_COORD * 8.0f;
3638
3639                                         /* check for fast mode */
3640                                         if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3641                                                 /* ugly hack to calculate extent for area lights, but only done once */
3642                                                 VectorScale( light->normal, -1.0f, dir );
3643                                                 for ( radius = 100.0f; radius < MAX_WORLD_COORD * 8.0f; radius += 10.0f )
3644                                                 {
3645                                                         float factor;
3646
3647                                                         VectorMA( light->origin, radius, light->normal, origin );
3648                                                         factor = PointToPolygonFormFactor( origin, dir, light->w );
3649                                                         if ( factor < 0.0f ) {
3650                                                                 factor *= -1.0f;
3651                                                         }
3652                                                         if ( ( factor * light->add ) <= light->falloffTolerance ) {
3653                                                                 light->envelope = radius;
3654                                                                 break;
3655                                                         }
3656                                                 }
3657                                         }
3658
3659                                         intensity = light->photons; /* hopefully not used */
3660                                 }
3661                                 else
3662                                 {
3663                                         radius = 0.0f;
3664                                         intensity = light->photons;
3665                                 }
3666
3667                                 /* other calcs */
3668                                 if ( light->envelope <= 0.0f ) {
3669                                         /* solve distance for non-distance lights */
3670                                         if ( !( light->flags & LIGHT_ATTEN_DISTANCE ) ) {
3671                                                 light->envelope = MAX_WORLD_COORD * 8.0f;
3672                                         }
3673
3674                                         else if ( ( light->flags & LIGHT_FAST ) || ( light->flags & LIGHT_FAST_TEMP ) ) {
3675                                                 /* solve distance for linear lights */
3676                                                 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3677                                                         light->envelope = ( ( intensity * linearScale ) - light->falloffTolerance ) / light->fade;
3678                                                 }
3679
3680                                                 /*
3681                                                    add = angle * light->photons * linearScale - (dist * light->fade);
3682                                                    T = (light->photons * linearScale) - (dist * light->fade);
3683                                                    T + (dist * light->fade) = (light->photons * linearScale);
3684                                                    dist * light->fade = (light->photons * linearScale) - T;
3685                                                    dist = ((light->photons * linearScale) - T) / light->fade;
3686                                                  */
3687
3688                                                 /* solve for inverse square falloff */
3689                                                 else{
3690                                                         light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;
3691                                                 }
3692
3693                                                 /*
3694                                                    add = light->photons / (dist * dist);
3695                                                    T = light->photons / (dist * dist);
3696                                                    T * (dist * dist) = light->photons;
3697                                                    dist = sqrt( light->photons / T );
3698                                                  */
3699                                         }
3700                                         else
3701                                         {
3702                                                 /* solve distance for linear lights */
3703                                                 if ( ( light->flags & LIGHT_ATTEN_LINEAR ) ) {
3704                                                         light->envelope = ( intensity * linearScale ) / light->fade;
3705                                                 }
3706
3707                                                 /* can't cull these */
3708                                                 else{
3709                                                         light->envelope = MAX_WORLD_COORD * 8.0f;
3710                                                 }
3711                                         }
3712                                 }
3713
3714                                 /* chop radius against pvs */
3715                                 {
3716                                         /* clear bounds */
3717                                         ClearBounds( mins, maxs );
3718
3719                                         /* check all leaves */
3720                                         for ( i = 0; i < numBSPLeafs; i++ )
3721                                         {
3722                                                 /* get test leaf */
3723                                                 leaf = &bspLeafs[ i ];
3724
3725                                                 /* in pvs? */
3726                                                 if ( leaf->cluster < 0 ) {
3727                                                         continue;
3728                                                 }
3729                                                 if ( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) { /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */
3730                                                         continue;
3731                                                 }
3732
3733                                                 /* add this leafs bbox to the bounds */
3734                                                 VectorCopy( leaf->mins, origin );
3735                                                 AddPointToBounds( origin, mins, maxs );
3736                                                 VectorCopy( leaf->maxs, origin );
3737                                                 AddPointToBounds( origin, mins, maxs );
3738                                         }
3739
3740                                         /* test to see if bounds encompass light */
3741                                         for ( i = 0; i < 3; i++ )
3742                                         {
3743                                                 if ( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] ) {
3744                                                         //% Sys_FPrintf( SYS_WRN, "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",
3745                                                         //%     mins[ 0 ], mins[ 1 ], mins[ 2 ],
3746                                                         //%     maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
3747                                                         //%     numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );
3748                                                         AddPointToBounds( light->origin, mins, maxs );
3749                                                 }
3750                                         }
3751
3752                                         /* chop the bounds by a plane for area lights and spotlights */
3753                                         if ( light->type == EMIT_AREA || light->type == EMIT_SPOT ) {
3754                                                 ChopBounds( mins, maxs, light->origin, light->normal );
3755                                         }
3756
3757                                         /* copy bounds */
3758                                         VectorCopy( mins, light->mins );
3759                                         VectorCopy( maxs, light->maxs );
3760
3761                                         /* reflect bounds around light origin */
3762                                         //%     VectorMA( light->origin, -1.0f, origin, origin );
3763                                         VectorScale( light->origin, 2, origin );
3764                                         VectorSubtract( origin, maxs, origin );
3765                                         AddPointToBounds( origin, mins, maxs );
3766                                         //%     VectorMA( light->origin, -1.0f, mins, origin );
3767                                         VectorScale( light->origin, 2, origin );
3768                                         VectorSubtract( origin, mins, origin );
3769                                         AddPointToBounds( origin, mins, maxs );
3770
3771                                         /* calculate spherical bounds */
3772                                         VectorSubtract( maxs, light->origin, dir );
3773                                         radius = (float) VectorLength( dir );
3774
3775                                         /* if this radius is smaller than the envelope, then set the envelope to it */
3776                                         if ( radius < light->envelope ) {
3777                                                 light->envelope = radius;
3778                                                 //%     Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );
3779                                         }
3780                                         //%     else
3781                                         //%             Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );
3782                                 }
3783
3784                                 /* add grid/surface only check */
3785                                 if ( forGrid ) {
3786                                         if ( !( light->flags & LIGHT_GRID ) ) {
3787                                                 light->envelope = 0.0f;
3788                                         }
3789                                 }
3790                                 else
3791                                 {
3792                                         if ( !( light->flags & LIGHT_SURFACES ) ) {
3793                                                 light->envelope = 0.0f;
3794                                         }
3795                                 }
3796                         }
3797
3798                         /* culled? */
3799                         if ( light->cluster < 0 || light->envelope <= 0.0f ) {
3800                                 /* debug code */
3801                                 //%     Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );
3802
3803                                 /* delete the light */
3804                                 numCulledLights++;
3805                                 *owner = light->next;
3806                                 if ( light->w != NULL ) {
3807                                         free( light->w );
3808                                 }
3809                                 free( light );
3810                                 continue;
3811                         }
3812                 }
3813
3814                 /* square envelope */
3815                 light->envelope2 = ( light->envelope * light->envelope );
3816
3817                 /* increment light count */
3818                 numLights++;
3819
3820                 /* set next light */
3821                 owner = &( ( **owner ).next );
3822         }
3823
3824         /* bucket sort lights by style */
3825         memset( buckets, 0, sizeof( buckets ) );
3826         light2 = NULL;
3827         for ( light = lights; light != NULL; light = light2 )
3828         {
3829                 /* get next light */
3830                 light2 = light->next;
3831
3832                 /* filter into correct bucket */
3833                 light->next = buckets[ light->style ];
3834                 buckets[ light->style ] = light;
3835
3836                 /* if any styled light is present, automatically set nocollapse */
3837                 if ( light->style != LS_NORMAL ) {
3838                         noCollapse = qtrue;
3839                 }
3840         }
3841
3842         /* filter back into light list */
3843         lights = NULL;
3844         for ( i = 255; i >= 0; i-- )
3845         {
3846                 light2 = NULL;
3847                 for ( light = buckets[ i ]; light != NULL; light = light2 )
3848                 {
3849                         light2 = light->next;
3850                         light->next = lights;
3851                         lights = light;
3852                 }
3853         }
3854
3855         /* emit some statistics */
3856         Sys_Printf( "%9d total lights\n", numLights );
3857         Sys_Printf( "%9d culled lights\n", numCulledLights );
3858 }
3859
3860
3861
3862 /*
3863    CreateTraceLightsForBounds()
3864    creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)
3865  */
3866
3867 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace ){
3868         int i;
3869         light_t     *light;
3870         vec3_t origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };
3871         float radius, dist, length;
3872
3873
3874         /* potential pre-setup  */
3875         if ( numLights == 0 ) {
3876                 SetupEnvelopes( qfalse, fast );
3877         }
3878
3879         /* debug code */
3880         //% 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 ] );
3881
3882         /* allocate the light list */
3883         trace->lights = safe_malloc( sizeof( light_t* ) * ( numLights + 1 ) );
3884         trace->numLights = 0;
3885
3886         /* calculate spherical bounds */
3887         VectorAdd( mins, maxs, origin );
3888         VectorScale( origin, 0.5f, origin );
3889         VectorSubtract( maxs, origin, dir );
3890         radius = (float) VectorLength( dir );
3891
3892         /* get length of normal vector */
3893         if ( normal != NULL ) {
3894                 length = VectorLength( normal );
3895         }
3896         else
3897         {
3898                 normal = nullVector;
3899                 length = 0;
3900         }
3901
3902         /* test each light and see if it reaches the sphere */
3903         /* note: the attenuation code MUST match LightingAtSample() */
3904         for ( light = lights; light; light = light->next )
3905         {
3906                 /* check zero sized envelope */
3907                 if ( light->envelope <= 0 ) {
3908                         lightsEnvelopeCulled++;
3909                         continue;
3910                 }
3911
3912                 /* check flags */
3913                 if ( !( light->flags & flags ) ) {
3914                         continue;
3915                 }
3916
3917                 /* sunlight skips all this nonsense */
3918                 if ( light->type != EMIT_SUN ) {
3919                         /* sun only? */
3920                         if ( sunOnly ) {
3921                                 continue;
3922                         }
3923
3924                         /* check against pvs cluster */
3925                         if ( numClusters > 0 && clusters != NULL ) {
3926                                 for ( i = 0; i < numClusters; i++ )
3927                                 {
3928                                         if ( ClusterVisible( light->cluster, clusters[ i ] ) ) {
3929                                                 break;
3930                                         }
3931                                 }
3932
3933                                 /* fixme! */
3934                                 if ( i == numClusters ) {
3935                                         lightsClusterCulled++;
3936                                         continue;
3937                                 }
3938                         }
3939
3940                         /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */
3941                         VectorSubtract( light->origin, origin, dir );
3942                         dist = VectorLength( dir );
3943                         dist -= light->envelope;
3944                         dist -= radius;
3945                         if ( dist > 0 ) {
3946                                 lightsEnvelopeCulled++;
3947                                 continue;
3948                         }
3949
3950                         /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */
3951                         #if 0
3952                         skip = qfalse;
3953                         for ( i = 0; i < 3; i++ )
3954                         {
3955                                 if ( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] ) {
3956                                         skip = qtrue;
3957                                 }
3958                         }
3959                         if ( skip ) {
3960                                 lightsBoundsCulled++;
3961                                 continue;
3962                         }
3963                         #endif
3964                 }
3965
3966                 /* planar surfaces (except twosided surfaces) have a couple more checks */
3967                 if ( length > 0.0f && trace->twoSided == qfalse ) {
3968                         /* lights coplanar with a surface won't light it */
3969                         if ( !( light->flags & LIGHT_TWOSIDED ) && DotProduct( light->normal, normal ) > 0.999f ) {
3970                                 lightsPlaneCulled++;
3971                                 continue;
3972                         }
3973
3974                         /* check to see if light is behind the plane */
3975                         if ( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f ) {
3976                                 lightsPlaneCulled++;
3977                                 continue;
3978                         }
3979                 }
3980
3981                 /* add this light */
3982                 trace->lights[ trace->numLights++ ] = light;
3983         }
3984
3985         /* make last night null */
3986         trace->lights[ trace->numLights ] = NULL;
3987 }
3988
3989
3990
3991 void FreeTraceLights( trace_t *trace ){
3992         if ( trace->lights != NULL ) {
3993                 free( trace->lights );
3994         }
3995 }
3996
3997
3998
3999 /*
4000    CreateTraceLightsForSurface()
4001    creates a list of lights that can potentially affect a drawsurface
4002  */
4003
4004 void CreateTraceLightsForSurface( int num, trace_t *trace ){
4005         int i;
4006         vec3_t mins, maxs, normal;
4007         bspDrawVert_t       *dv;
4008         bspDrawSurface_t    *ds;
4009         surfaceInfo_t       *info;
4010
4011
4012         /* dummy check */
4013         if ( num < 0 ) {
4014                 return;
4015         }
4016
4017         /* get drawsurface and info */
4018         ds = &bspDrawSurfaces[ num ];
4019         info = &surfaceInfos[ num ];
4020
4021         /* get the mins/maxs for the dsurf */
4022         ClearBounds( mins, maxs );
4023         VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );
4024         for ( i = 0; i < ds->numVerts; i++ )
4025         {
4026                 dv = &yDrawVerts[ ds->firstVert + i ];
4027                 AddPointToBounds( dv->xyz, mins, maxs );
4028                 if ( !VectorCompare( dv->normal, normal ) ) {
4029                         VectorClear( normal );
4030                 }
4031         }
4032
4033         /* create the lights for the bounding box */
4034         CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );
4035 }
4036
4037 /////////////////////////////////////////////////////////////
4038
4039 #define FLOODLIGHT_CONE_ANGLE           88  /* degrees */
4040 #define FLOODLIGHT_NUM_ANGLE_STEPS      16
4041 #define FLOODLIGHT_NUM_ELEVATION_STEPS  4
4042 #define FLOODLIGHT_NUM_VECTORS          ( FLOODLIGHT_NUM_ANGLE_STEPS * FLOODLIGHT_NUM_ELEVATION_STEPS )
4043
4044 static vec3_t floodVectors[ FLOODLIGHT_NUM_VECTORS ];
4045 static int numFloodVectors = 0;
4046
4047 void SetupFloodLight( void ){
4048         int i, j;
4049         float angle, elevation, angleStep, elevationStep;
4050         const char  *value;
4051         double v1,v2,v3,v4,v5,v6;
4052
4053         /* note it */
4054         Sys_FPrintf( SYS_VRB, "--- SetupFloodLight ---\n" );
4055
4056         /* calculate angular steps */
4057         angleStep = DEG2RAD( 360.0f / FLOODLIGHT_NUM_ANGLE_STEPS );
4058         elevationStep = DEG2RAD( FLOODLIGHT_CONE_ANGLE / FLOODLIGHT_NUM_ELEVATION_STEPS );
4059
4060         /* iterate angle */
4061         angle = 0.0f;
4062         for ( i = 0, angle = 0.0f; i < FLOODLIGHT_NUM_ANGLE_STEPS; i++, angle += angleStep )
4063         {
4064                 /* iterate elevation */
4065                 for ( j = 0, elevation = elevationStep * 0.5f; j < FLOODLIGHT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
4066                 {
4067                         floodVectors[ numFloodVectors ][ 0 ] = sin( elevation ) * cos( angle );
4068                         floodVectors[ numFloodVectors ][ 1 ] = sin( elevation ) * sin( angle );
4069                         floodVectors[ numFloodVectors ][ 2 ] = cos( elevation );
4070                         numFloodVectors++;
4071                 }
4072         }
4073
4074         /* emit some statistics */
4075         Sys_FPrintf( SYS_VRB, "%9d numFloodVectors\n", numFloodVectors );
4076
4077         /* floodlight */
4078         value = ValueForKey( &entities[ 0 ], "_floodlight" );
4079
4080         if ( value[ 0 ] != '\0' ) {
4081                 v1 = v2 = v3 = 0;
4082                 v4 = floodlightDistance;
4083                 v5 = floodlightIntensity;
4084                 v6 = floodlightDirectionScale;
4085
4086                 sscanf( value, "%lf %lf %lf %lf %lf %lf", &v1, &v2, &v3, &v4, &v5, &v6 );
4087
4088                 floodlightRGB[0] = v1;
4089                 floodlightRGB[1] = v2;
4090                 floodlightRGB[2] = v3;
4091
4092                 if ( VectorLength( floodlightRGB ) == 0 ) {
4093                         VectorSet( floodlightRGB,0.94,0.94,1.0 );
4094                 }
4095
4096                 if ( v4 < 1 ) {
4097                         v4 = 1024;
4098                 }
4099                 if ( v5 < 1 ) {
4100                         v5 = 128;
4101                 }
4102                 if ( v6 < 0 ) {
4103                         v6 = 1;
4104                 }
4105
4106                 floodlightDistance = v4;
4107                 floodlightIntensity = v5;
4108                 floodlightDirectionScale = v6;
4109
4110                 floodlighty = qtrue;
4111                 Sys_Printf( "FloodLighting enabled via worldspawn _floodlight key.\n" );
4112         }
4113         else
4114         {
4115                 VectorSet( floodlightRGB,0.94,0.94,1.0 );
4116         }
4117         if ( colorsRGB ) {
4118                 floodlightRGB[0] = Image_LinearFloatFromsRGBFloat( floodlightRGB[0] );
4119                 floodlightRGB[1] = Image_LinearFloatFromsRGBFloat( floodlightRGB[1] );
4120                 floodlightRGB[2] = Image_LinearFloatFromsRGBFloat( floodlightRGB[2] );
4121         }
4122         ColorNormalize( floodlightRGB,floodlightRGB );
4123 }
4124
4125 /*
4126    FloodLightForSample()
4127    calculates floodlight value for a given sample
4128    once again, kudos to the dirtmapping coder
4129  */
4130
4131 float FloodLightForSample( trace_t *trace, float floodLightDistance, qboolean floodLightLowQuality ){
4132         int i;
4133         float d;
4134         float contribution;
4135         int sub = 0;
4136         float gatherLight, outLight;
4137         vec3_t normal, worldUp, myUp, myRt, direction, displacement;
4138         float dd;
4139         int vecs = 0;
4140
4141         gatherLight = 0;
4142         /* dummy check */
4143         //if( !dirty )
4144         //      return 1.0f;
4145         if ( trace == NULL || trace->cluster < 0 ) {
4146                 return 0.0f;
4147         }
4148
4149
4150         /* setup */
4151         dd = floodLightDistance;
4152         VectorCopy( trace->normal, normal );
4153
4154         /* check if the normal is aligned to the world-up */
4155         if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f && ( normal[ 2 ] == 1.0f || normal[ 2 ] == -1.0f ) ) {
4156                 if ( normal[ 2 ] == 1.0f ) {
4157                         VectorSet( myRt, 1.0f, 0.0f, 0.0f );
4158                         VectorSet( myUp, 0.0f, 1.0f, 0.0f );
4159                 }
4160                 else if ( normal[ 2 ] == -1.0f ) {
4161                         VectorSet( myRt, -1.0f, 0.0f, 0.0f );
4162                         VectorSet( myUp,  0.0f, 1.0f, 0.0f );
4163                 }
4164         }
4165         else
4166         {
4167                 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
4168                 CrossProduct( normal, worldUp, myRt );
4169                 VectorNormalize( myRt, myRt );
4170                 CrossProduct( myRt, normal, myUp );
4171                 VectorNormalize( myUp, myUp );
4172         }
4173
4174         /* vortex: optimise floodLightLowQuality a bit */
4175         if ( floodLightLowQuality == qtrue ) {
4176                 /* iterate through ordered vectors */
4177                 for ( i = 0; i < numFloodVectors; i++ )
4178                         if ( rand() % 10 != 0 ) {
4179                                 continue;
4180                         }
4181         }
4182         else
4183         {
4184                 /* iterate through ordered vectors */
4185                 for ( i = 0; i < numFloodVectors; i++ )
4186                 {
4187                         vecs++;
4188
4189                         /* transform vector into tangent space */
4190                         direction[ 0 ] = myRt[ 0 ] * floodVectors[ i ][ 0 ] + myUp[ 0 ] * floodVectors[ i ][ 1 ] + normal[ 0 ] * floodVectors[ i ][ 2 ];
4191                         direction[ 1 ] = myRt[ 1 ] * floodVectors[ i ][ 0 ] + myUp[ 1 ] * floodVectors[ i ][ 1 ] + normal[ 1 ] * floodVectors[ i ][ 2 ];
4192                         direction[ 2 ] = myRt[ 2 ] * floodVectors[ i ][ 0 ] + myUp[ 2 ] * floodVectors[ i ][ 1 ] + normal[ 2 ] * floodVectors[ i ][ 2 ];
4193
4194                         /* set endpoint */
4195                         VectorMA( trace->origin, dd, direction, trace->end );
4196
4197                         //VectorMA( trace->origin, 1, direction, trace->origin );
4198
4199                         SetupTrace( trace );
4200                         VectorSet(trace->color, 1.0f, 1.0f, 1.0f);
4201                         /* trace */
4202                         TraceLine( trace );
4203                         contribution = 1;
4204
4205                         if ( trace->compileFlags & C_SKY || trace->compileFlags & C_TRANSLUCENT ) {
4206                                 contribution = 1.0f;
4207                         }
4208                         else if ( trace->opaque ) {
4209                                 VectorSubtract( trace->hit, trace->origin, displacement );
4210                                 d = VectorLength( displacement );
4211
4212                                 // d=trace->distance;
4213                                 //if (d>256) gatherDirt+=1;
4214                                 contribution = d / dd;
4215                                 if ( contribution > 1 ) {
4216                                         contribution = 1.0f;
4217                                 }
4218
4219                                 //gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
4220                         }
4221
4222                         gatherLight += contribution;
4223                 }
4224         }
4225
4226         /* early out */
4227         if ( gatherLight <= 0.0f ) {
4228                 return 0.0f;
4229         }
4230
4231         sub = vecs;
4232
4233         if ( sub < 1 ) {
4234                 sub = 1;
4235         }
4236         gatherLight /= ( sub );
4237
4238         outLight = gatherLight;
4239         if ( outLight > 1.0f ) {
4240                 outLight = 1.0f;
4241         }
4242
4243         /* return to sender */
4244         return outLight;
4245 }
4246
4247 /*
4248    FloodLightRawLightmap
4249    lighttracer style ambient occlusion light hack.
4250    Kudos to the dirtmapping author for most of this source.
4251    VorteX: modified to floodlight up custom surfaces (q3map_floodLight)
4252    VorteX: fixed problems with deluxemapping
4253  */
4254
4255 // floodlight pass on a lightmap
4256 void FloodLightRawLightmapPass( rawLightmap_t *lm, vec3_t lmFloodLightRGB, float lmFloodLightIntensity, float lmFloodLightDistance, qboolean lmFloodLightLowQuality, float floodlightDirectionScale ){
4257         int i, x, y, *cluster;
4258         float               *origin, *normal, *floodlight, floodLightAmount;
4259         surfaceInfo_t       *info;
4260         trace_t trace;
4261         // int sx, sy;
4262         // float samples, average, *floodlight2;
4263
4264         memset( &trace,0,sizeof( trace_t ) );
4265
4266         /* setup trace */
4267         trace.testOcclusion = qtrue;
4268         trace.forceSunlight = qfalse;
4269         trace.twoSided = qtrue;
4270         trace.recvShadows = lm->recvShadows;
4271         trace.numSurfaces = lm->numLightSurfaces;
4272         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
4273         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
4274         trace.testAll = qfalse;
4275         trace.distance = 1024;
4276
4277         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
4278         //trace.twoSided = qfalse;
4279         for ( i = 0; i < trace.numSurfaces; i++ )
4280         {
4281                 /* get surface */
4282                 info = &surfaceInfos[ trace.surfaces[ i ] ];
4283
4284                 /* check twosidedness */
4285                 if ( info->si->twoSided ) {
4286                         trace.twoSided = qtrue;
4287                         break;
4288                 }
4289         }
4290
4291         /* gather floodlight */
4292         for ( y = 0; y < lm->sh; y++ )
4293         {
4294                 for ( x = 0; x < lm->sw; x++ )
4295                 {
4296                         /* get luxel */
4297                         cluster = SUPER_CLUSTER( x, y );
4298                         origin = SUPER_ORIGIN( x, y );
4299                         normal = SUPER_NORMAL( x, y );
4300                         floodlight = SUPER_FLOODLIGHT( x, y );
4301
4302                         /* set default dirt */
4303                         *floodlight = 0.0f;
4304
4305                         /* only look at mapped luxels */
4306                         if ( *cluster < 0 ) {
4307                                 continue;
4308                         }
4309
4310                         /* copy to trace */
4311                         trace.cluster = *cluster;
4312                         VectorCopy( origin, trace.origin );
4313                         VectorCopy( normal, trace.normal );
4314
4315                         /* get floodlight */
4316                         floodLightAmount = FloodLightForSample( &trace, lmFloodLightDistance, lmFloodLightLowQuality ) * lmFloodLightIntensity;
4317
4318                         /* add floodlight */
4319                         floodlight[0] += lmFloodLightRGB[0] * floodLightAmount;
4320                         floodlight[1] += lmFloodLightRGB[1] * floodLightAmount;
4321                         floodlight[2] += lmFloodLightRGB[2] * floodLightAmount;
4322                         floodlight[3] += floodlightDirectionScale;
4323                 }
4324         }
4325
4326         /* testing no filtering */
4327         return;
4328
4329 #if 0
4330
4331         /* filter "dirt" */
4332         for ( y = 0; y < lm->sh; y++ )
4333         {
4334                 for ( x = 0; x < lm->sw; x++ )
4335                 {
4336                         /* get luxel */
4337                         cluster = SUPER_CLUSTER( x, y );
4338                         floodlight = SUPER_FLOODLIGHT( x, y );
4339
4340                         /* filter dirt by adjacency to unmapped luxels */
4341                         average = *floodlight;
4342                         samples = 1.0f;
4343                         for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
4344                         {
4345                                 if ( sy < 0 || sy >= lm->sh ) {
4346                                         continue;
4347                                 }
4348
4349                                 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
4350                                 {
4351                                         if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
4352                                                 continue;
4353                                         }
4354
4355                                         /* get neighboring luxel */
4356                                         cluster = SUPER_CLUSTER( sx, sy );
4357                                         floodlight2 = SUPER_FLOODLIGHT( sx, sy );
4358                                         if ( *cluster < 0 || *floodlight2 <= 0.0f ) {
4359                                                 continue;
4360                                         }
4361
4362                                         /* add it */
4363                                         average += *floodlight2;
4364                                         samples += 1.0f;
4365                                 }
4366
4367                                 /* bail */
4368                                 if ( samples <= 0.0f ) {
4369                                         break;
4370                                 }
4371                         }
4372
4373                         /* bail */
4374                         if ( samples <= 0.0f ) {
4375                                 continue;
4376                         }
4377
4378                         /* scale dirt */
4379                         *floodlight = average / samples;
4380                 }
4381         }
4382 #endif
4383 }
4384
4385 void FloodLightRawLightmap( int rawLightmapNum ){
4386         rawLightmap_t       *lm;
4387
4388         /* bail if this number exceeds the number of raw lightmaps */
4389         if ( rawLightmapNum >= numRawLightmaps ) {
4390                 return;
4391         }
4392         /* get lightmap */
4393         lm = &rawLightmaps[ rawLightmapNum ];
4394
4395         /* global pass */
4396         if ( floodlighty && floodlightIntensity ) {
4397                 FloodLightRawLightmapPass( lm, floodlightRGB, floodlightIntensity, floodlightDistance, floodlight_lowquality, floodlightDirectionScale );
4398         }
4399
4400         /* custom pass */
4401         if ( lm->floodlightIntensity ) {
4402                 FloodLightRawLightmapPass( lm, lm->floodlightRGB, lm->floodlightIntensity, lm->floodlightDistance, qfalse, lm->floodlightDirectionScale );
4403                 numSurfacesFloodlighten += 1;
4404         }
4405 }
4406
4407 void FloodlightRawLightmaps(){
4408         Sys_Printf( "--- FloodlightRawLightmap ---\n" );
4409         numSurfacesFloodlighten = 0;
4410         RunThreadsOnIndividual( numRawLightmaps, qtrue, FloodLightRawLightmap );
4411         Sys_Printf( "%9d custom lightmaps floodlighted\n", numSurfacesFloodlighten );
4412 }
4413
4414 /*
4415    FloodLightIlluminate()
4416    illuminate floodlight into lightmap luxels
4417  */
4418
4419 void FloodlightIlluminateLightmap( rawLightmap_t *lm ){
4420         float               *luxel, *floodlight, *deluxel, *normal;
4421         int                 *cluster;
4422         float brightness;
4423         int x, y, lightmapNum;
4424
4425         /* walk lightmaps */
4426         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
4427         {
4428                 /* early out */
4429                 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
4430                         continue;
4431                 }
4432
4433                 /* apply floodlight to each luxel */
4434                 for ( y = 0; y < lm->sh; y++ )
4435                 {
4436                         for ( x = 0; x < lm->sw; x++ )
4437                         {
4438                                 /* get floodlight */
4439                                 floodlight = SUPER_FLOODLIGHT( x, y );
4440                                 if ( !floodlight[0] && !floodlight[1] && !floodlight[2] ) {
4441                                         continue;
4442                                 }
4443
4444                                 /* get cluster */
4445                                 cluster = SUPER_CLUSTER( x, y );
4446
4447                                 /* only process mapped luxels */
4448                                 if ( *cluster < 0 ) {
4449                                         continue;
4450                                 }
4451
4452                                 /* get particulars */
4453                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
4454                                 deluxel = SUPER_DELUXEL( x, y );
4455
4456                                 /* add to lightmap */
4457                                 luxel[0] += floodlight[0];
4458                                 luxel[1] += floodlight[1];
4459                                 luxel[2] += floodlight[2];
4460
4461                                 if ( luxel[3] == 0 ) {
4462                                         luxel[3] = 1;
4463                                 }
4464
4465                                 /* add to deluxemap */
4466                                 if ( deluxemap && floodlight[3] > 0 ) {
4467                                         vec3_t lightvector;
4468
4469                                         normal = SUPER_NORMAL( x, y );
4470                                         brightness = RGBTOGRAY( floodlight ) * ( 1.0f / 255.0f ) * floodlight[3];
4471
4472                                         // use AT LEAST this amount of contribution from ambient for the deluxemap, fixes points that receive ZERO light
4473                                         if ( brightness < 0.00390625f ) {
4474                                                 brightness = 0.00390625f;
4475                                         }
4476
4477                                         VectorScale( normal, brightness, lightvector );
4478                                         VectorAdd( deluxel, lightvector, deluxel );
4479                                 }
4480                         }
4481                 }
4482         }
4483 }