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