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