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