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