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