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