fix warning: unused variable 'foo'
[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         int i;
49         float max, gamma;
50         vec3_t sample;
51
52
53         /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */
54         if ( scale <= 0.0f ) {
55                 scale = 1.0f;
56         }
57
58         /* make a local copy */
59         VectorScale( color, scale, sample );
60
61         /* muck with it */
62         gamma = 1.0f / lightmapGamma;
63         for ( i = 0; i < 3; i++ )
64         {
65                 /* handle negative light */
66                 if ( sample[ i ] < 0.0f ) {
67                         sample[ i ] = 0.0f;
68                         continue;
69                 }
70
71                 /* gamma */
72                 sample[ i ] = pow( sample[ i ] / 255.0f, gamma ) * 255.0f;
73         }
74
75         /* clamp with color normalization */
76         max = sample[ 0 ];
77         if ( sample[ 1 ] > max ) {
78                 max = sample[ 1 ];
79         }
80         if ( sample[ 2 ] > max ) {
81                 max = sample[ 2 ];
82         }
83         if ( max > 255.0f ) {
84                 VectorScale( sample, ( 255.0f / max ), sample );
85         }
86
87         /* compensate for ingame overbrighting/bitshifting */
88         VectorScale( sample, ( 1.0f / lightmapCompensate ), sample );
89
90         /* store it off */
91         colorBytes[ 0 ] = sample[ 0 ];
92         colorBytes[ 1 ] = sample[ 1 ];
93         colorBytes[ 2 ] = sample[ 2 ];
94 }
95
96
97
98 /* -------------------------------------------------------------------------------
99
100    this section deals with phong shading (normal interpolation across brush faces)
101
102    ------------------------------------------------------------------------------- */
103
104 /*
105    SmoothNormals()
106    smooths together coincident vertex normals across the bsp
107  */
108
109 #define MAX_SAMPLES             256
110 #define THETA_EPSILON           0.000001
111 #define EQUAL_NORMAL_EPSILON    0.01
112
113 void SmoothNormals( void ){
114         int i, j, k, f, cs, numVerts, numVotes, fOld, start;
115         float shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;
116         bspDrawSurface_t    *ds;
117         shaderInfo_t        *si;
118         float               *shadeAngles;
119         byte                *smoothed;
120         vec3_t average, diff;
121         int indexes[ MAX_SAMPLES ];
122         vec3_t votes[ MAX_SAMPLES ];
123
124
125         /* allocate shade angle table */
126         shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );
127         memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );
128
129         /* allocate smoothed table */
130         cs = ( numBSPDrawVerts / 8 ) + 1;
131         smoothed = safe_malloc( cs );
132         memset( smoothed, 0, cs );
133
134         /* set default shade angle */
135         defaultShadeAngle = DEG2RAD( shadeAngleDegrees );
136         maxShadeAngle = 0;
137
138         /* run through every surface and flag verts belonging to non-lightmapped surfaces
139            and set per-vertex smoothing angle */
140         for ( i = 0; i < numBSPDrawSurfaces; i++ )
141         {
142                 /* get drawsurf */
143                 ds = &bspDrawSurfaces[ i ];
144
145                 /* get shader for shade angle */
146                 si = surfaceInfos[ i ].si;
147                 if ( si->shadeAngleDegrees ) {
148                         shadeAngle = DEG2RAD( si->shadeAngleDegrees );
149                 }
150                 else{
151                         shadeAngle = defaultShadeAngle;
152                 }
153                 if ( shadeAngle > maxShadeAngle ) {
154                         maxShadeAngle = shadeAngle;
155                 }
156
157                 /* flag its verts */
158                 for ( j = 0; j < ds->numVerts; j++ )
159                 {
160                         f = ds->firstVert + j;
161                         shadeAngles[ f ] = shadeAngle;
162                         if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
163                                 smoothed[ f >> 3 ] |= ( 1 << ( f & 7 ) );
164                         }
165                 }
166
167                 /* ydnar: optional force-to-trisoup */
168                 if ( trisoup && ds->surfaceType == MST_PLANAR ) {
169                         ds->surfaceType = MST_TRIANGLE_SOUP;
170                         ds->lightmapNum[ 0 ] = -3;
171                 }
172         }
173
174         /* bail if no surfaces have a shade angle */
175         if ( maxShadeAngle == 0 ) {
176                 free( shadeAngles );
177                 free( smoothed );
178                 return;
179         }
180
181         /* init pacifier */
182         fOld = -1;
183         start = I_FloatTime();
184
185         /* go through the list of vertexes */
186         for ( i = 0; i < numBSPDrawVerts; i++ )
187         {
188                 /* print pacifier */
189                 f = 10 * i / numBSPDrawVerts;
190                 if ( f != fOld ) {
191                         fOld = f;
192                         Sys_Printf( "%i...", f );
193                 }
194
195                 /* already smoothed? */
196                 if ( smoothed[ i >> 3 ] & ( 1 << ( i & 7 ) ) ) {
197                         continue;
198                 }
199
200                 /* clear */
201                 VectorClear( average );
202                 numVerts = 0;
203                 numVotes = 0;
204
205                 /* build a table of coincident vertexes */
206                 for ( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )
207                 {
208                         /* already smoothed? */
209                         if ( smoothed[ j >> 3 ] & ( 1 << ( j & 7 ) ) ) {
210                                 continue;
211                         }
212
213                         /* test vertexes */
214                         if ( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse ) {
215                                 continue;
216                         }
217
218                         /* use smallest shade angle */
219                         shadeAngle = ( shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ] );
220
221                         /* check shade angle */
222                         dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );
223                         if ( dot > 1.0 ) {
224                                 dot = 1.0;
225                         }
226                         else if ( dot < -1.0 ) {
227                                 dot = -1.0;
228                         }
229                         testAngle = acos( dot ) + THETA_EPSILON;
230                         if ( testAngle >= shadeAngle ) {
231                                 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
232                                 continue;
233                         }
234                         //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );
235
236                         /* add to the list */
237                         indexes[ numVerts++ ] = j;
238
239                         /* flag vertex */
240                         smoothed[ j >> 3 ] |= ( 1 << ( j & 7 ) );
241
242                         /* see if this normal has already been voted */
243                         for ( k = 0; k < numVotes; k++ )
244                         {
245                                 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );
246                                 if ( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&
247                                          fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&
248                                          fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON ) {
249                                         break;
250                                 }
251                         }
252
253                         /* add a new vote? */
254                         if ( k == numVotes && numVotes < MAX_SAMPLES ) {
255                                 VectorAdd( average, bspDrawVerts[ j ].normal, average );
256                                 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );
257                                 numVotes++;
258                         }
259                 }
260
261                 /* don't average for less than 2 verts */
262                 if ( numVerts < 2 ) {
263                         continue;
264                 }
265
266                 /* average normal */
267                 if ( VectorNormalize( average, average ) > 0 ) {
268                         /* smooth */
269                         for ( j = 0; j < numVerts; j++ )
270                                 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );
271                 }
272         }
273
274         /* free the tables */
275         free( shadeAngles );
276         free( smoothed );
277
278         /* print time */
279         Sys_Printf( " (%i)\n", (int) ( I_FloatTime() - start ) );
280 }
281
282
283
284 /* -------------------------------------------------------------------------------
285
286    this section deals with phong shaded lightmap tracing
287
288    ------------------------------------------------------------------------------- */
289
290 /* 9th rewrite (recursive subdivision of a lightmap triangle) */
291
292 /*
293    CalcTangentVectors()
294    calculates the st tangent vectors for normalmapping
295  */
296
297 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv ){
298         int i;
299         float bb, s, t;
300         vec3_t bary;
301
302
303         /* calculate barycentric basis for the triangle */
304         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 ] );
305         if ( fabs( bb ) < 0.00000001f ) {
306                 return qfalse;
307         }
308
309         /* do each vertex */
310         for ( i = 0; i < numVerts; i++ )
311         {
312                 /* calculate s tangent vector */
313                 s = dv[ i ]->st[ 0 ] + 10.0f;
314                 t = dv[ i ]->st[ 1 ];
315                 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
316                 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
317                 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
318
319                 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
320                 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
321                 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
322
323                 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );
324                 VectorNormalize( stv[ i ], stv[ i ] );
325
326                 /* calculate t tangent vector */
327                 s = dv[ i ]->st[ 0 ];
328                 t = dv[ i ]->st[ 1 ] + 10.0f;
329                 bary[ 0 ] = ( ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) - ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) ) / bb;
330                 bary[ 1 ] = ( ( dv[ 2 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) - ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 2 ]->st[ 1 ] - t ) ) / bb;
331                 bary[ 2 ] = ( ( dv[ 0 ]->st[ 0 ] - s ) * ( dv[ 1 ]->st[ 1 ] - t ) - ( dv[ 1 ]->st[ 0 ] - s ) * ( dv[ 0 ]->st[ 1 ] - t ) ) / bb;
332
333                 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];
334                 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];
335                 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];
336
337                 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );
338                 VectorNormalize( ttv[ i ], ttv[ i ] );
339
340                 /* debug code */
341                 //%     Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,
342                 //%             stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );
343         }
344
345         /* return to caller */
346         return qtrue;
347 }
348
349
350
351
352 /*
353    PerturbNormal()
354    perterbs the normal by the shader's normalmap in tangent space
355  */
356
357 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] ){
358         int i;
359         vec4_t bump;
360
361
362         /* passthrough */
363         VectorCopy( dv->normal, pNormal );
364
365         /* sample normalmap */
366         if ( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse ) {
367                 return;
368         }
369
370         /* remap sampled normal from [0,255] to [-1,-1] */
371         for ( i = 0; i < 3; i++ )
372                 bump[ i ] = ( bump[ i ] - 127.0f ) * ( 1.0f / 127.5f );
373
374         /* scale tangent vectors and add to original normal */
375         VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );
376         VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );
377         VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );
378
379         /* renormalize and return */
380         VectorNormalize( pNormal, pNormal );
381 }
382
383
384
385 /*
386    MapSingleLuxel()
387    maps a luxel for triangle bv at
388  */
389
390 #define NUDGE           0.5f
391 #define BOGUS_NUDGE     -99999.0f
392
393 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 ] ){
394         int i, x, y, numClusters, *clusters, pointCluster, *cluster;
395         float           *luxel, *origin, *normal, d, lightmapSampleOffset;
396         shaderInfo_t    *si;
397         vec3_t pNormal;
398         vec3_t vecs[ 3 ];
399         vec3_t nudged;
400         float           *nudge;
401         static float nudges[][ 2 ] =
402         {
403                 //%{ 0, 0 },            /* try center first */
404                 { -NUDGE, 0 },                      /* left */
405                 { NUDGE, 0 },                       /* right */
406                 { 0, NUDGE },                       /* up */
407                 { 0, -NUDGE },                      /* down */
408                 { -NUDGE, NUDGE },                  /* left/up */
409                 { NUDGE, -NUDGE },                  /* right/down */
410                 { NUDGE, NUDGE },                   /* right/up */
411                 { -NUDGE, -NUDGE },                 /* left/down */
412                 { BOGUS_NUDGE, BOGUS_NUDGE }
413         };
414
415
416         /* find luxel xy coords (fixme: subtract 0.5?) */
417         x = dv->lightmap[ 0 ][ 0 ];
418         y = dv->lightmap[ 0 ][ 1 ];
419         if ( x < 0 ) {
420                 x = 0;
421         }
422         else if ( x >= lm->sw ) {
423                 x = lm->sw - 1;
424         }
425         if ( y < 0 ) {
426                 y = 0;
427         }
428         else if ( y >= lm->sh ) {
429                 y = lm->sh - 1;
430         }
431
432         /* set shader and cluster list */
433         if ( info != NULL ) {
434                 si = info->si;
435                 numClusters = info->numSurfaceClusters;
436                 clusters = &surfaceClusters[ info->firstSurfaceCluster ];
437         }
438         else
439         {
440                 si = NULL;
441                 numClusters = 0;
442                 clusters = NULL;
443         }
444
445         /* get luxel, origin, cluster, and normal */
446         luxel = SUPER_LUXEL( 0, x, y );
447         origin = SUPER_ORIGIN( x, y );
448         normal = SUPER_NORMAL( x, y );
449         cluster = SUPER_CLUSTER( x, y );
450
451         /* don't attempt to remap occluded luxels for planar surfaces */
452         if ( ( *cluster ) == CLUSTER_OCCLUDED && lm->plane != NULL ) {
453                 return ( *cluster );
454         }
455
456         /* only average the normal for premapped luxels */
457         else if ( ( *cluster ) >= 0 ) {
458                 /* do bumpmap calculations */
459                 if ( stv != NULL ) {
460                         PerturbNormal( dv, si, pNormal, stv, ttv );
461                 }
462                 else{
463                         VectorCopy( dv->normal, pNormal );
464                 }
465
466                 /* add the additional normal data */
467                 VectorAdd( normal, pNormal, normal );
468                 luxel[ 3 ] += 1.0f;
469                 return ( *cluster );
470         }
471
472         /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */
473
474         /* get origin */
475
476         /* axial lightmap projection */
477         if ( lm->vecs != NULL ) {
478                 /* calculate an origin for the sample from the lightmap vectors */
479                 VectorCopy( lm->origin, origin );
480                 for ( i = 0; i < 3; i++ )
481                 {
482                         /* add unless it's the axis, which is taken care of later */
483                         if ( i == lm->axisNum ) {
484                                 continue;
485                         }
486                         origin[ i ] += ( x * lm->vecs[ 0 ][ i ] ) + ( y * lm->vecs[ 1 ][ i ] );
487                 }
488
489                 /* project the origin onto the plane */
490                 d = DotProduct( origin, plane ) - plane[ 3 ];
491                 d /= plane[ lm->axisNum ];
492                 origin[ lm->axisNum ] -= d;
493         }
494
495         /* non axial lightmap projection (explicit xyz) */
496         else{
497                 VectorCopy( dv->xyz, origin );
498         }
499
500         /* planar surfaces have precalculated lightmap vectors for nudging */
501         if ( lm->plane != NULL ) {
502                 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );
503                 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );
504                 VectorCopy( lm->plane, vecs[ 2 ] );
505         }
506
507         /* non-planar surfaces must calculate them */
508         else
509         {
510                 if ( plane != NULL ) {
511                         VectorCopy( plane, vecs[ 2 ] );
512                 }
513                 else{
514                         VectorCopy( dv->normal, vecs[ 2 ] );
515                 }
516                 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );
517         }
518
519         /* push the origin off the surface a bit */
520         if ( si != NULL ) {
521                 lightmapSampleOffset = si->lightmapSampleOffset;
522         }
523         else{
524                 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;
525         }
526         if ( lm->axisNum < 0 ) {
527                 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );
528         }
529         else if ( vecs[ 2 ][ lm->axisNum ] < 0.0f ) {
530                 origin[ lm->axisNum ] -= lightmapSampleOffset;
531         }
532         else{
533                 origin[ lm->axisNum ] += lightmapSampleOffset;
534         }
535
536         /* get cluster */
537         pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );
538
539         /* another retarded hack, storing nudge count in luxel[ 1 ] */
540         luxel[ 1 ] = 0.0f;
541
542         /* point in solid? (except in dark mode) */
543         if ( pointCluster < 0 && dark == qfalse ) {
544                 /* nudge the the location around */
545                 nudge = nudges[ 0 ];
546                 while ( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )
547                 {
548                         /* nudge the vector around a bit */
549                         for ( i = 0; i < 3; i++ )
550                         {
551                                 /* set nudged point*/
552                                 nudged[ i ] = origin[ i ] + ( nudge[ 0 ] * vecs[ 0 ][ i ] ) + ( nudge[ 1 ] * vecs[ 1 ][ i ] );
553                         }
554                         nudge += 2;
555
556                         /* get pvs cluster */
557                         pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );
558                         if ( pointCluster >= 0 ) {
559                                 VectorCopy( nudged, origin );
560                         }
561                         luxel[ 1 ] += 1.0f;
562                 }
563         }
564
565         /* as a last resort, if still in solid, try drawvert origin offset by normal (except in dark mode) */
566         if ( pointCluster < 0 && si != NULL && dark == qfalse ) {
567                 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );
568                 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );
569                 if ( pointCluster >= 0 ) {
570                         VectorCopy( nudged, origin );
571                 }
572                 luxel[ 1 ] += 1.0f;
573         }
574
575         /* valid? */
576         if ( pointCluster < 0 ) {
577                 ( *cluster ) = CLUSTER_OCCLUDED;
578                 VectorClear( origin );
579                 VectorClear( normal );
580                 numLuxelsOccluded++;
581                 return ( *cluster );
582         }
583
584         /* debug code */
585         //%     Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );
586
587         /* do bumpmap calculations */
588         if ( stv ) {
589                 PerturbNormal( dv, si, pNormal, stv, ttv );
590         }
591         else{
592                 VectorCopy( dv->normal, pNormal );
593         }
594
595         /* store the cluster and normal */
596         ( *cluster ) = pointCluster;
597         VectorCopy( pNormal, normal );
598
599         /* store explicit mapping pass and implicit mapping pass */
600         luxel[ 0 ] = pass;
601         luxel[ 3 ] = 1.0f;
602
603         /* add to count */
604         numLuxelsMapped++;
605
606         /* return ok */
607         return ( *cluster );
608 }
609
610
611
612 /*
613    MapTriangle_r()
614    recursively subdivides a triangle until its edges are shorter
615    than the distance between two luxels (thanks jc :)
616  */
617
618 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 ] ){
619         bspDrawVert_t mid, *dv2[ 3 ];
620         int max;
621
622
623         /* map the vertexes */
624         #if 0
625         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
626         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
627         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
628         #endif
629
630         /* subdivide calc */
631         {
632                 int i;
633                 float       *a, *b, dx, dy, dist, maxDist;
634
635
636                 /* find the longest edge and split it */
637                 max = -1;
638                 maxDist = 0;
639                 for ( i = 0; i < 3; i++ )
640                 {
641                         /* get verts */
642                         a = dv[ i ]->lightmap[ 0 ];
643                         b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
644
645                         /* get dists */
646                         dx = a[ 0 ] - b[ 0 ];
647                         dy = a[ 1 ] - b[ 1 ];
648                         dist = ( dx * dx ) + ( dy * dy );   //% sqrt( (dx * dx) + (dy * dy) );
649
650                         /* longer? */
651                         if ( dist > maxDist ) {
652                                 maxDist = dist;
653                                 max = i;
654                         }
655                 }
656
657                 /* try to early out */
658                 if ( max < 0 || maxDist <= subdivideThreshold ) { /* ydnar: was i < 0 instead of max < 0 (?) */
659                         return;
660                 }
661         }
662
663         /* split the longest edge and map it */
664         LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 3 ], &mid );
665         MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );
666
667         /* push the point up a little bit to account for fp creep (fixme: revisit this) */
668         //%     VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );
669
670         /* recurse to first triangle */
671         VectorCopy( dv, dv2 );
672         dv2[ max ] = &mid;
673         MapTriangle_r( lm, info, dv2, plane, stv, ttv );
674
675         /* recurse to second triangle */
676         VectorCopy( dv, dv2 );
677         dv2[ ( max + 1 ) % 3 ] = &mid;
678         MapTriangle_r( lm, info, dv2, plane, stv, ttv );
679 }
680
681
682
683 /*
684    MapTriangle()
685    seed function for MapTriangle_r()
686    requires a cw ordered triangle
687  */
688
689 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial ){
690         int i;
691         vec4_t plane;
692         vec3_t          *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];
693
694
695         /* get plane if possible */
696         if ( lm->plane != NULL ) {
697                 VectorCopy( lm->plane, plane );
698                 plane[ 3 ] = lm->plane[ 3 ];
699         }
700
701         /* otherwise make one from the points */
702         else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
703                 return qfalse;
704         }
705
706         /* check to see if we need to calculate texture->world tangent vectors */
707         if ( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) ) {
708                 stv = stvStatic;
709                 ttv = ttvStatic;
710         }
711         else
712         {
713                 stv = NULL;
714                 ttv = NULL;
715         }
716
717         /* map the vertexes */
718         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
719         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
720         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
721
722         /* 2002-11-20: prefer axial triangle edges */
723         if ( mapNonAxial ) {
724                 /* subdivide the triangle */
725                 MapTriangle_r( lm, info, dv, plane, stv, ttv );
726                 return qtrue;
727         }
728
729         for ( i = 0; i < 3; i++ )
730         {
731                 float           *a, *b;
732                 bspDrawVert_t   *dv2[ 3 ];
733
734
735                 /* get verts */
736                 a = dv[ i ]->lightmap[ 0 ];
737                 b = dv[ ( i + 1 ) % 3 ]->lightmap[ 0 ];
738
739                 /* make degenerate triangles for mapping edges */
740                 if ( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f ) {
741                         dv2[ 0 ] = dv[ i ];
742                         dv2[ 1 ] = dv[ ( i + 1 ) % 3 ];
743                         dv2[ 2 ] = dv[ ( i + 1 ) % 3 ];
744
745                         /* map the degenerate triangle */
746                         MapTriangle_r( lm, info, dv2, plane, stv, ttv );
747                 }
748         }
749
750         return qtrue;
751 }
752
753
754
755 /*
756    MapQuad_r()
757    recursively subdivides a quad until its edges are shorter
758    than the distance between two luxels
759  */
760
761 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 ] ){
762         bspDrawVert_t mid[ 2 ], *dv2[ 4 ];
763         int max;
764
765
766         /* subdivide calc */
767         {
768                 int i;
769                 float       *a, *b, dx, dy, dist, maxDist;
770
771
772                 /* find the longest edge and split it */
773                 max = -1;
774                 maxDist = 0;
775                 for ( i = 0; i < 4; i++ )
776                 {
777                         /* get verts */
778                         a = dv[ i ]->lightmap[ 0 ];
779                         b = dv[ ( i + 1 ) % 4 ]->lightmap[ 0 ];
780
781                         /* get dists */
782                         dx = a[ 0 ] - b[ 0 ];
783                         dy = a[ 1 ] - b[ 1 ];
784                         dist = ( dx * dx ) + ( dy * dy );   //% sqrt( (dx * dx) + (dy * dy) );
785
786                         /* longer? */
787                         if ( dist > maxDist ) {
788                                 maxDist = dist;
789                                 max = i;
790                         }
791                 }
792
793                 /* try to early out */
794                 if ( max < 0 || maxDist <= subdivideThreshold ) {
795                         return;
796                 }
797         }
798
799         /* we only care about even/odd edges */
800         max &= 1;
801
802         /* split the longest edges */
803         LerpDrawVert( dv[ max ], dv[ ( max + 1 ) % 4 ], &mid[ 0 ] );
804         LerpDrawVert( dv[ max + 2 ], dv[ ( max + 3 ) % 4 ], &mid[ 1 ] );
805
806         /* map the vertexes */
807         MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );
808         MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );
809
810         /* 0 and 2 */
811         if ( max == 0 ) {
812                 /* recurse to first quad */
813                 dv2[ 0 ] = dv[ 0 ];
814                 dv2[ 1 ] = &mid[ 0 ];
815                 dv2[ 2 ] = &mid[ 1 ];
816                 dv2[ 3 ] = dv[ 3 ];
817                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
818
819                 /* recurse to second quad */
820                 dv2[ 0 ] = &mid[ 0 ];
821                 dv2[ 1 ] = dv[ 1 ];
822                 dv2[ 2 ] = dv[ 2 ];
823                 dv2[ 3 ] = &mid[ 1 ];
824                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
825         }
826
827         /* 1 and 3 */
828         else
829         {
830                 /* recurse to first quad */
831                 dv2[ 0 ] = dv[ 0 ];
832                 dv2[ 1 ] = dv[ 1 ];
833                 dv2[ 2 ] = &mid[ 0 ];
834                 dv2[ 3 ] = &mid[ 1 ];
835                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
836
837                 /* recurse to second quad */
838                 dv2[ 0 ] = &mid[ 1 ];
839                 dv2[ 1 ] = &mid[ 0 ];
840                 dv2[ 2 ] = dv[ 2 ];
841                 dv2[ 3 ] = dv[ 3 ];
842                 MapQuad_r( lm, info, dv2, plane, stv, ttv );
843         }
844 }
845
846
847
848 /*
849    MapQuad()
850    seed function for MapQuad_r()
851    requires a cw ordered triangle quad
852  */
853
854 #define QUAD_PLANAR_EPSILON     0.5f
855
856 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] ){
857         float dist;
858         vec4_t plane;
859         vec3_t          *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];
860
861
862         /* get plane if possible */
863         if ( lm->plane != NULL ) {
864                 VectorCopy( lm->plane, plane );
865                 plane[ 3 ] = lm->plane[ 3 ];
866         }
867
868         /* otherwise make one from the points */
869         else if ( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse ) {
870                 return qfalse;
871         }
872
873         /* 4th point must fall on the plane */
874         dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];
875         if ( fabs( dist ) > QUAD_PLANAR_EPSILON ) {
876                 return qfalse;
877         }
878
879         /* check to see if we need to calculate texture->world tangent vectors */
880         if ( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) ) {
881                 stv = stvStatic;
882                 ttv = ttvStatic;
883         }
884         else
885         {
886                 stv = NULL;
887                 ttv = NULL;
888         }
889
890         /* map the vertexes */
891         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );
892         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );
893         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );
894         MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );
895
896         /* subdivide the quad */
897         MapQuad_r( lm, info, dv, plane, stv, ttv );
898         return qtrue;
899 }
900
901
902
903 /*
904    MapRawLightmap()
905    maps the locations, normals, and pvs clusters for a raw lightmap
906  */
907
908 #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)
909
910 void MapRawLightmap( int rawLightmapNum ){
911         int n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;
912         float               *luxel, *origin, *normal, samples, radius, pass;
913         rawLightmap_t       *lm;
914         bspDrawSurface_t    *ds;
915         surfaceInfo_t       *info;
916         mesh_t src, *subdivided, *mesh;
917         bspDrawVert_t       *verts, *dv[ 4 ], fake;
918
919
920         /* bail if this number exceeds the number of raw lightmaps */
921         if ( rawLightmapNum >= numRawLightmaps ) {
922                 return;
923         }
924
925         /* get lightmap */
926         lm = &rawLightmaps[ rawLightmapNum ];
927
928         /* -----------------------------------------------------------------
929            map referenced surfaces onto the raw lightmap
930            ----------------------------------------------------------------- */
931
932         /* walk the list of surfaces on this raw lightmap */
933         for ( n = 0; n < lm->numLightSurfaces; n++ )
934         {
935                 /* with > 1 surface per raw lightmap, clear occluded */
936                 if ( n > 0 ) {
937                         for ( y = 0; y < lm->sh; y++ )
938                         {
939                                 for ( x = 0; x < lm->sw; x++ )
940                                 {
941                                         /* get cluster */
942                                         cluster = SUPER_CLUSTER( x, y );
943                                         if ( *cluster < 0 ) {
944                                                 *cluster = CLUSTER_UNMAPPED;
945                                         }
946                                 }
947                         }
948                 }
949
950                 /* get surface */
951                 num = lightSurfaces[ lm->firstLightSurface + n ];
952                 ds = &bspDrawSurfaces[ num ];
953                 info = &surfaceInfos[ num ];
954
955                 /* bail if no lightmap to calculate */
956                 if ( info->lm != lm ) {
957                         Sys_Printf( "!" );
958                         continue;
959                 }
960
961                 /* map the surface onto the lightmap origin/cluster/normal buffers */
962                 switch ( ds->surfaceType )
963                 {
964                 case MST_PLANAR:
965                         /* get verts */
966                         verts = yDrawVerts + ds->firstVert;
967
968                         /* map the triangles */
969                         for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
970                         {
971                                 for ( i = 0; i < ds->numIndexes; i += 3 )
972                                 {
973                                         dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];
974                                         dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];
975                                         dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];
976                                         MapTriangle( lm, info, dv, mapNonAxial );
977                                 }
978                         }
979                         break;
980
981                 case MST_PATCH:
982                         /* make a mesh from the drawsurf */
983                         src.width = ds->patchWidth;
984                         src.height = ds->patchHeight;
985                         src.verts = &yDrawVerts[ ds->firstVert ];
986                         //%     subdivided = SubdivideMesh( src, 8, 512 );
987                         subdivided = SubdivideMesh2( src, info->patchIterations );
988
989                         /* fit it to the curve and remove colinear verts on rows/columns */
990                         PutMeshOnCurve( *subdivided );
991                         mesh = RemoveLinearMeshColumnsRows( subdivided );
992                         FreeMesh( subdivided );
993
994                         /* get verts */
995                         verts = mesh->verts;
996
997                         /* debug code */
998                                 #if 0
999                         if ( lm->plane ) {
1000                                 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",
1001                                                         lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],
1002                                                         lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],
1003                                                         lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );
1004                         }
1005                                 #endif
1006
1007                         /* map the mesh quads */
1008                                 #if 0
1009
1010                         for ( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )
1011                         {
1012                                 for ( y = 0; y < ( mesh->height - 1 ); y++ )
1013                                 {
1014                                         for ( x = 0; x < ( mesh->width - 1 ); x++ )
1015                                         {
1016                                                 /* set indexes */
1017                                                 pw[ 0 ] = x + ( y * mesh->width );
1018                                                 pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1019                                                 pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1020                                                 pw[ 3 ] = x + 1 + ( y * mesh->width );
1021                                                 pw[ 4 ] = x + ( y * mesh->width );      /* same as pw[ 0 ] */
1022
1023                                                 /* set radix */
1024                                                 r = ( x + y ) & 1;
1025
1026                                                 /* get drawverts and map first triangle */
1027                                                 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1028                                                 dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1029                                                 dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1030                                                 MapTriangle( lm, info, dv, mapNonAxial );
1031
1032                                                 /* get drawverts and map second triangle */
1033                                                 dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1034                                                 dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1035                                                 dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1036                                                 MapTriangle( lm, info, dv, mapNonAxial );
1037                                         }
1038                                 }
1039                         }
1040
1041                                 #else
1042
1043                         for ( y = 0; y < ( mesh->height - 1 ); y++ )
1044                         {
1045                                 for ( x = 0; x < ( mesh->width - 1 ); x++ )
1046                                 {
1047                                         /* set indexes */
1048                                         pw[ 0 ] = x + ( y * mesh->width );
1049                                         pw[ 1 ] = x + ( ( y + 1 ) * mesh->width );
1050                                         pw[ 2 ] = x + 1 + ( ( y + 1 ) * mesh->width );
1051                                         pw[ 3 ] = x + 1 + ( y * mesh->width );
1052                                         pw[ 4 ] = pw[ 0 ];
1053
1054                                         /* set radix */
1055                                         r = ( x + y ) & 1;
1056
1057                                         /* attempt to map quad first */
1058                                         dv[ 0 ] = &verts[ pw[ r + 0 ] ];
1059                                         dv[ 1 ] = &verts[ pw[ r + 1 ] ];
1060                                         dv[ 2 ] = &verts[ pw[ r + 2 ] ];
1061                                         dv[ 3 ] = &verts[ pw[ r + 3 ] ];
1062                                         if ( MapQuad( lm, info, dv ) ) {
1063                                                 continue;
1064                                         }
1065
1066                                         /* get drawverts and map first triangle */
1067                                         MapTriangle( lm, info, dv, mapNonAxial );
1068
1069                                         /* get drawverts and map second triangle */
1070                                         dv[ 1 ] = &verts[ pw[ r + 2 ] ];
1071                                         dv[ 2 ] = &verts[ pw[ r + 3 ] ];
1072                                         MapTriangle( lm, info, dv, mapNonAxial );
1073                                 }
1074                         }
1075
1076                                 #endif
1077
1078                         /* free the mesh */
1079                         FreeMesh( mesh );
1080                         break;
1081
1082                 default:
1083                         break;
1084                 }
1085         }
1086
1087         /* -----------------------------------------------------------------
1088            average and clean up luxel normals
1089            ----------------------------------------------------------------- */
1090
1091         /* walk the luxels */
1092         for ( y = 0; y < lm->sh; y++ )
1093         {
1094                 for ( x = 0; x < lm->sw; x++ )
1095                 {
1096                         /* get luxel */
1097                         luxel = SUPER_LUXEL( 0, x, y );
1098                         normal = SUPER_NORMAL( x, y );
1099                         cluster = SUPER_CLUSTER( x, y );
1100
1101                         /* only look at mapped luxels */
1102                         if ( *cluster < 0 ) {
1103                                 continue;
1104                         }
1105
1106                         /* the normal data could be the sum of multiple samples */
1107                         if ( luxel[ 3 ] > 1.0f ) {
1108                                 VectorNormalize( normal, normal );
1109                         }
1110
1111                         /* mark this luxel as having only one normal */
1112                         luxel[ 3 ] = 1.0f;
1113                 }
1114         }
1115
1116         /* non-planar surfaces stop here */
1117         if ( lm->plane == NULL ) {
1118                 return;
1119         }
1120
1121         /* -----------------------------------------------------------------
1122            map occluded or unuxed luxels
1123            ----------------------------------------------------------------- */
1124
1125         /* walk the luxels */
1126         radius = floor( superSample / 2 );
1127         radius = radius > 0 ? radius : 1.0f;
1128         radius += 1.0f;
1129         for ( pass = 2.0f; pass <= radius; pass += 1.0f )
1130         {
1131                 for ( y = 0; y < lm->sh; y++ )
1132                 {
1133                         for ( x = 0; x < lm->sw; x++ )
1134                         {
1135                                 /* get luxel */
1136                                 luxel = SUPER_LUXEL( 0, x, y );
1137                                 normal = SUPER_NORMAL( x, y );
1138                                 cluster = SUPER_CLUSTER( x, y );
1139
1140                                 /* only look at unmapped luxels */
1141                                 if ( *cluster != CLUSTER_UNMAPPED ) {
1142                                         continue;
1143                                 }
1144
1145                                 /* divine a normal and origin from neighboring luxels */
1146                                 VectorClear( fake.xyz );
1147                                 VectorClear( fake.normal );
1148                                 fake.lightmap[ 0 ][ 0 ] = x;    //% 0.0001 + x;
1149                                 fake.lightmap[ 0 ][ 1 ] = y;    //% 0.0001 + y;
1150                                 samples = 0.0f;
1151                                 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1152                                 {
1153                                         if ( sy < 0 || sy >= lm->sh ) {
1154                                                 continue;
1155                                         }
1156
1157                                         for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1158                                         {
1159                                                 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1160                                                         continue;
1161                                                 }
1162
1163                                                 /* get neighboring luxel */
1164                                                 luxel = SUPER_LUXEL( 0, sx, sy );
1165                                                 origin = SUPER_ORIGIN( sx, sy );
1166                                                 normal = SUPER_NORMAL( sx, sy );
1167                                                 cluster = SUPER_CLUSTER( sx, sy );
1168
1169                                                 /* only consider luxels mapped in previous passes */
1170                                                 if ( *cluster < 0 || luxel[ 0 ] >= pass ) {
1171                                                         continue;
1172                                                 }
1173
1174                                                 /* add its distinctiveness to our own */
1175                                                 VectorAdd( fake.xyz, origin, fake.xyz );
1176                                                 VectorAdd( fake.normal, normal, fake.normal );
1177                                                 samples += luxel[ 3 ];
1178                                         }
1179                                 }
1180
1181                                 /* any samples? */
1182                                 if ( samples == 0.0f ) {
1183                                         continue;
1184                                 }
1185
1186                                 /* average */
1187                                 VectorDivide( fake.xyz, samples, fake.xyz );
1188                                 //%     VectorDivide( fake.normal, samples, fake.normal );
1189                                 if ( VectorNormalize( fake.normal, fake.normal ) == 0.0f ) {
1190                                         continue;
1191                                 }
1192
1193                                 /* map the fake vert */
1194                                 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );
1195                         }
1196                 }
1197         }
1198
1199         /* -----------------------------------------------------------------
1200            average and clean up luxel normals
1201            ----------------------------------------------------------------- */
1202
1203         /* walk the luxels */
1204         for ( y = 0; y < lm->sh; y++ )
1205         {
1206                 for ( x = 0; x < lm->sw; x++ )
1207                 {
1208                         /* get luxel */
1209                         luxel = SUPER_LUXEL( 0, x, y );
1210                         normal = SUPER_NORMAL( x, y );
1211                         cluster = SUPER_CLUSTER( x, y );
1212
1213                         /* only look at mapped luxels */
1214                         if ( *cluster < 0 ) {
1215                                 continue;
1216                         }
1217
1218                         /* the normal data could be the sum of multiple samples */
1219                         if ( luxel[ 3 ] > 1.0f ) {
1220                                 VectorNormalize( normal, normal );
1221                         }
1222
1223                         /* mark this luxel as having only one normal */
1224                         luxel[ 3 ] = 1.0f;
1225                 }
1226         }
1227
1228         /* debug code */
1229         #if 0
1230         Sys_Printf( "\n" );
1231         for ( y = 0; y < lm->sh; y++ )
1232         {
1233                 for ( x = 0; x < lm->sw; x++ )
1234                 {
1235                         vec3_t mins, maxs;
1236
1237
1238                         cluster = SUPER_CLUSTER( x, y );
1239                         origin = SUPER_ORIGIN( x, y );
1240                         normal = SUPER_NORMAL( x, y );
1241                         luxel = SUPER_LUXEL( x, y );
1242
1243                         if ( *cluster < 0 ) {
1244                                 continue;
1245                         }
1246
1247                         /* check if within the bounding boxes of all surfaces referenced */
1248                         ClearBounds( mins, maxs );
1249                         for ( n = 0; n < lm->numLightSurfaces; n++ )
1250                         {
1251                                 int TOL;
1252                                 info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];
1253                                 TOL = info->sampleSize + 2;
1254                                 AddPointToBounds( info->mins, mins, maxs );
1255                                 AddPointToBounds( info->maxs, mins, maxs );
1256                                 if ( origin[ 0 ] > ( info->mins[ 0 ] - TOL ) && origin[ 0 ] < ( info->maxs[ 0 ] + TOL ) &&
1257                                          origin[ 1 ] > ( info->mins[ 1 ] - TOL ) && origin[ 1 ] < ( info->maxs[ 1 ] + TOL ) &&
1258                                          origin[ 2 ] > ( info->mins[ 2 ] - TOL ) && origin[ 2 ] < ( info->maxs[ 2 ] + TOL ) ) {
1259                                         break;
1260                                 }
1261                         }
1262
1263                         /* inside? */
1264                         if ( n < lm->numLightSurfaces ) {
1265                                 continue;
1266                         }
1267
1268                         /* report bogus origin */
1269                         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",
1270                                                 rawLightmapNum, x, y, *cluster,
1271                                                 origin[ 0 ], origin[ 1 ], origin[ 2 ],
1272                                                 mins[ 0 ], mins[ 1 ], mins[ 2 ],
1273                                                 maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],
1274                                                 luxel[ 3 ] );
1275                 }
1276         }
1277         #endif
1278 }
1279
1280
1281
1282 /*
1283    SetupDirt()
1284    sets up dirtmap (ambient occlusion)
1285  */
1286
1287 #define DIRT_CONE_ANGLE             88  /* degrees */
1288 #define DIRT_NUM_ANGLE_STEPS        16
1289 #define DIRT_NUM_ELEVATION_STEPS    3
1290 #define DIRT_NUM_VECTORS            ( DIRT_NUM_ANGLE_STEPS * DIRT_NUM_ELEVATION_STEPS )
1291
1292 static vec3_t dirtVectors[ DIRT_NUM_VECTORS ];
1293 static int numDirtVectors = 0;
1294
1295 void SetupDirt( void ){
1296         int i, j;
1297         float angle, elevation, angleStep, elevationStep;
1298
1299
1300         /* note it */
1301         Sys_FPrintf( SYS_VRB, "--- SetupDirt ---\n" );
1302
1303         /* calculate angular steps */
1304         angleStep = DEG2RAD( 360.0f / DIRT_NUM_ANGLE_STEPS );
1305         elevationStep = DEG2RAD( DIRT_CONE_ANGLE / DIRT_NUM_ELEVATION_STEPS );
1306
1307         /* iterate angle */
1308         angle = 0.0f;
1309         for ( i = 0, angle = 0.0f; i < DIRT_NUM_ANGLE_STEPS; i++, angle += angleStep )
1310         {
1311                 /* iterate elevation */
1312                 for ( j = 0, elevation = elevationStep * 0.5f; j < DIRT_NUM_ELEVATION_STEPS; j++, elevation += elevationStep )
1313                 {
1314                         dirtVectors[ numDirtVectors ][ 0 ] = sin( elevation ) * cos( angle );
1315                         dirtVectors[ numDirtVectors ][ 1 ] = sin( elevation ) * sin( angle );
1316                         dirtVectors[ numDirtVectors ][ 2 ] = cos( elevation );
1317                         numDirtVectors++;
1318                 }
1319         }
1320
1321         /* emit some statistics */
1322         Sys_FPrintf( SYS_VRB, "%9d dirtmap vectors\n", numDirtVectors );
1323 }
1324
1325
1326 /*
1327    DirtForSample()
1328    calculates dirt value for a given sample
1329  */
1330
1331 float DirtForSample( trace_t *trace ){
1332         int i;
1333         float gatherDirt, outDirt, angle, elevation, ooDepth;
1334         vec3_t normal, worldUp, myUp, myRt, temp, direction, displacement;
1335
1336
1337         /* dummy check */
1338         if ( !dirty ) {
1339                 return 1.0f;
1340         }
1341         if ( trace == NULL || trace->cluster < 0 ) {
1342                 return 0.0f;
1343         }
1344
1345         /* setup */
1346         gatherDirt = 0.0f;
1347         ooDepth = 1.0f / dirtDepth;
1348         VectorCopy( trace->normal, normal );
1349
1350         /* check if the normal is aligned to the world-up */
1351         if ( normal[ 0 ] == 0.0f && normal[ 1 ] == 0.0f ) {
1352                 if ( normal[ 2 ] == 1.0f ) {
1353                         VectorSet( myRt, 1.0f, 0.0f, 0.0f );
1354                         VectorSet( myUp, 0.0f, 1.0f, 0.0f );
1355                 }
1356                 else if ( normal[ 2 ] == -1.0f ) {
1357                         VectorSet( myRt, -1.0f, 0.0f, 0.0f );
1358                         VectorSet( myUp,  0.0f, 1.0f, 0.0f );
1359                 }
1360         }
1361         else
1362         {
1363                 VectorSet( worldUp, 0.0f, 0.0f, 1.0f );
1364                 CrossProduct( normal, worldUp, myRt );
1365                 VectorNormalize( myRt, myRt );
1366                 CrossProduct( myRt, normal, myUp );
1367                 VectorNormalize( myUp, myUp );
1368         }
1369
1370         /* 1 = random mode, 0 (well everything else) = non-random mode */
1371         if ( dirtMode == 1 ) {
1372                 /* iterate */
1373                 for ( i = 0; i < numDirtVectors; i++ )
1374                 {
1375                         /* get random vector */
1376                         angle = Random() * DEG2RAD( 360.0f );
1377                         elevation = Random() * DEG2RAD( DIRT_CONE_ANGLE );
1378                         temp[ 0 ] = cos( angle ) * sin( elevation );
1379                         temp[ 1 ] = sin( angle ) * sin( elevation );
1380                         temp[ 2 ] = cos( elevation );
1381
1382                         /* transform into tangent space */
1383                         direction[ 0 ] = myRt[ 0 ] * temp[ 0 ] + myUp[ 0 ] * temp[ 1 ] + normal[ 0 ] * temp[ 2 ];
1384                         direction[ 1 ] = myRt[ 1 ] * temp[ 0 ] + myUp[ 1 ] * temp[ 1 ] + normal[ 1 ] * temp[ 2 ];
1385                         direction[ 2 ] = myRt[ 2 ] * temp[ 0 ] + myUp[ 2 ] * temp[ 1 ] + normal[ 2 ] * temp[ 2 ];
1386
1387                         /* set endpoint */
1388                         VectorMA( trace->origin, dirtDepth, direction, trace->end );
1389                         SetupTrace( trace );
1390
1391                         /* trace */
1392                         TraceLine( trace );
1393                         if ( trace->opaque ) {
1394                                 VectorSubtract( trace->hit, trace->origin, displacement );
1395                                 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1396                         }
1397                 }
1398         }
1399         else
1400         {
1401                 /* iterate through ordered vectors */
1402                 for ( i = 0; i < numDirtVectors; i++ )
1403                 {
1404                         /* transform vector into tangent space */
1405                         direction[ 0 ] = myRt[ 0 ] * dirtVectors[ i ][ 0 ] + myUp[ 0 ] * dirtVectors[ i ][ 1 ] + normal[ 0 ] * dirtVectors[ i ][ 2 ];
1406                         direction[ 1 ] = myRt[ 1 ] * dirtVectors[ i ][ 0 ] + myUp[ 1 ] * dirtVectors[ i ][ 1 ] + normal[ 1 ] * dirtVectors[ i ][ 2 ];
1407                         direction[ 2 ] = myRt[ 2 ] * dirtVectors[ i ][ 0 ] + myUp[ 2 ] * dirtVectors[ i ][ 1 ] + normal[ 2 ] * dirtVectors[ i ][ 2 ];
1408
1409                         /* set endpoint */
1410                         VectorMA( trace->origin, dirtDepth, direction, trace->end );
1411                         SetupTrace( trace );
1412
1413                         /* trace */
1414                         TraceLine( trace );
1415                         if ( trace->opaque ) {
1416                                 VectorSubtract( trace->hit, trace->origin, displacement );
1417                                 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1418                         }
1419                 }
1420         }
1421
1422         /* direct ray */
1423         VectorMA( trace->origin, dirtDepth, normal, trace->end );
1424         SetupTrace( trace );
1425
1426         /* trace */
1427         TraceLine( trace );
1428         if ( trace->opaque ) {
1429                 VectorSubtract( trace->hit, trace->origin, displacement );
1430                 gatherDirt += 1.0f - ooDepth * VectorLength( displacement );
1431         }
1432
1433         /* early out */
1434         if ( gatherDirt <= 0.0f ) {
1435                 return 1.0f;
1436         }
1437
1438         /* apply gain (does this even do much? heh) */
1439         outDirt = pow( gatherDirt / ( numDirtVectors + 1 ), dirtGain );
1440         if ( outDirt > 1.0f ) {
1441                 outDirt = 1.0f;
1442         }
1443
1444         /* apply scale */
1445         outDirt *= dirtScale;
1446         if ( outDirt > 1.0f ) {
1447                 outDirt = 1.0f;
1448         }
1449
1450         /* return to sender */
1451         return 1.0f - outDirt;
1452 }
1453
1454
1455
1456 /*
1457    DirtyRawLightmap()
1458    calculates dirty fraction for each luxel
1459  */
1460
1461 void DirtyRawLightmap( int rawLightmapNum ){
1462         int i, x, y, sx, sy, *cluster;
1463         float               *origin, *normal, *dirt, *dirt2, average, samples;
1464         rawLightmap_t       *lm;
1465         surfaceInfo_t       *info;
1466         trace_t trace;
1467
1468
1469         /* bail if this number exceeds the number of raw lightmaps */
1470         if ( rawLightmapNum >= numRawLightmaps ) {
1471                 return;
1472         }
1473
1474         /* get lightmap */
1475         lm = &rawLightmaps[ rawLightmapNum ];
1476
1477         /* setup trace */
1478         trace.testOcclusion = qtrue;
1479         trace.forceSunlight = qfalse;
1480         trace.recvShadows = lm->recvShadows;
1481         trace.numSurfaces = lm->numLightSurfaces;
1482         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1483         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1484         trace.testAll = qfalse;
1485
1486         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1487         trace.twoSided = qfalse;
1488         for ( i = 0; i < trace.numSurfaces; i++ )
1489         {
1490                 /* get surface */
1491                 info = &surfaceInfos[ trace.surfaces[ i ] ];
1492
1493                 /* check twosidedness */
1494                 if ( info->si->twoSided ) {
1495                         trace.twoSided = qtrue;
1496                         break;
1497                 }
1498         }
1499
1500         /* gather dirt */
1501         for ( y = 0; y < lm->sh; y++ )
1502         {
1503                 for ( x = 0; x < lm->sw; x++ )
1504                 {
1505                         /* get luxel */
1506                         cluster = SUPER_CLUSTER( x, y );
1507                         origin = SUPER_ORIGIN( x, y );
1508                         normal = SUPER_NORMAL( x, y );
1509                         dirt = SUPER_DIRT( x, y );
1510
1511                         /* set default dirt */
1512                         *dirt = 0.0f;
1513
1514                         /* only look at mapped luxels */
1515                         if ( *cluster < 0 ) {
1516                                 continue;
1517                         }
1518
1519                         /* copy to trace */
1520                         trace.cluster = *cluster;
1521                         VectorCopy( origin, trace.origin );
1522                         VectorCopy( normal, trace.normal );
1523
1524                         /* get dirt */
1525                         *dirt = DirtForSample( &trace );
1526                 }
1527         }
1528
1529         /* testing no filtering */
1530         //%     return;
1531
1532         /* filter dirt */
1533         for ( y = 0; y < lm->sh; y++ )
1534         {
1535                 for ( x = 0; x < lm->sw; x++ )
1536                 {
1537                         /* get luxel */
1538                         cluster = SUPER_CLUSTER( x, y );
1539                         dirt = SUPER_DIRT( x, y );
1540
1541                         /* filter dirt by adjacency to unmapped luxels */
1542                         average = *dirt;
1543                         samples = 1.0f;
1544                         for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
1545                         {
1546                                 if ( sy < 0 || sy >= lm->sh ) {
1547                                         continue;
1548                                 }
1549
1550                                 for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
1551                                 {
1552                                         if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
1553                                                 continue;
1554                                         }
1555
1556                                         /* get neighboring luxel */
1557                                         cluster = SUPER_CLUSTER( sx, sy );
1558                                         dirt2 = SUPER_DIRT( sx, sy );
1559                                         if ( *cluster < 0 || *dirt2 <= 0.0f ) {
1560                                                 continue;
1561                                         }
1562
1563                                         /* add it */
1564                                         average += *dirt2;
1565                                         samples += 1.0f;
1566                                 }
1567
1568                                 /* bail */
1569                                 if ( samples <= 0.0f ) {
1570                                         break;
1571                                 }
1572                         }
1573
1574                         /* bail */
1575                         if ( samples <= 0.0f ) {
1576                                 continue;
1577                         }
1578
1579                         /* scale dirt */
1580                         *dirt = average / samples;
1581                 }
1582         }
1583 }
1584
1585
1586
1587 /*
1588    SubmapRawLuxel()
1589    calculates the pvs cluster, origin, normal of a sub-luxel
1590  */
1591
1592 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal ){
1593         int i, *cluster, *cluster2;
1594         float       *origin, *origin2, *normal; //%     , *normal2;
1595         vec3_t originVecs[ 2 ];                 //%     , normalVecs[ 2 ];
1596
1597
1598         /* calulate x vector */
1599         if ( ( x < ( lm->sw - 1 ) && bx >= 0.0f ) || ( x == 0 && bx <= 0.0f ) ) {
1600                 cluster = SUPER_CLUSTER( x, y );
1601                 origin = SUPER_ORIGIN( x, y );
1602                 //%     normal = SUPER_NORMAL( x, y );
1603                 cluster2 = SUPER_CLUSTER( x + 1, y );
1604                 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );
1605                 //%     normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );
1606         }
1607         else if ( ( x > 0 && bx <= 0.0f ) || ( x == ( lm->sw - 1 ) && bx >= 0.0f ) ) {
1608                 cluster = SUPER_CLUSTER( x - 1, y );
1609                 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );
1610                 //%     normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );
1611                 cluster2 = SUPER_CLUSTER( x, y );
1612                 origin2 = SUPER_ORIGIN( x, y );
1613                 //%     normal2 = SUPER_NORMAL( x, y );
1614         }
1615         else{
1616                 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );
1617         }
1618
1619         VectorSubtract( origin2, origin, originVecs[ 0 ] );
1620         //%     VectorSubtract( normal2, normal, normalVecs[ 0 ] );
1621
1622         /* calulate y vector */
1623         if ( ( y < ( lm->sh - 1 ) && bx >= 0.0f ) || ( y == 0 && bx <= 0.0f ) ) {
1624                 cluster = SUPER_CLUSTER( x, y );
1625                 origin = SUPER_ORIGIN( x, y );
1626                 //%     normal = SUPER_NORMAL( x, y );
1627                 cluster2 = SUPER_CLUSTER( x, y + 1 );
1628                 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );
1629                 //%     normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );
1630         }
1631         else if ( ( y > 0 && bx <= 0.0f ) || ( y == ( lm->sh - 1 ) && bx >= 0.0f ) ) {
1632                 cluster = SUPER_CLUSTER( x, y - 1 );
1633                 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );
1634                 //%     normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );
1635                 cluster2 = SUPER_CLUSTER( x, y );
1636                 origin2 = SUPER_ORIGIN( x, y );
1637                 //%     normal2 = SUPER_NORMAL( x, y );
1638         }
1639         else{
1640                 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );
1641         }
1642
1643         VectorSubtract( origin2, origin, originVecs[ 1 ] );
1644         //%     VectorSubtract( normal2, normal, normalVecs[ 1 ] );
1645
1646         /* calculate new origin */
1647         //%     VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );
1648         //%     VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );
1649         for ( i = 0; i < 3; i++ )
1650                 sampleOrigin[ i ] = sampleOrigin[ i ] + ( bx * originVecs[ 0 ][ i ] ) + ( by * originVecs[ 1 ][ i ] );
1651
1652         /* get cluster */
1653         *sampleCluster = ClusterForPointExtFilter( sampleOrigin, ( LUXEL_EPSILON * 2 ), lm->numLightClusters, lm->lightClusters );
1654         if ( *sampleCluster < 0 ) {
1655                 return qfalse;
1656         }
1657
1658         /* calculate new normal */
1659         //%     VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );
1660         //%     VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );
1661         //%     if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )
1662         //%             return qfalse;
1663         normal = SUPER_NORMAL( x, y );
1664         VectorCopy( normal, sampleNormal );
1665
1666         /* return ok */
1667         return qtrue;
1668 }
1669
1670
1671 /*
1672    SubsampleRawLuxel_r()
1673    recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached
1674  */
1675
1676 static void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel ){
1677         int b, samples, mapped, lighted;
1678         int cluster[ 4 ];
1679         vec4_t luxel[ 4 ];
1680         vec3_t origin[ 4 ], normal[ 4 ];
1681         float biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };
1682         vec3_t color, total;
1683
1684
1685         /* limit check */
1686         if ( lightLuxel[ 3 ] >= lightSamples ) {
1687                 return;
1688         }
1689
1690         /* setup */
1691         VectorClear( total );
1692         mapped = 0;
1693         lighted = 0;
1694
1695         /* make 2x2 subsample stamp */
1696         for ( b = 0; b < 4; b++ )
1697         {
1698                 /* set origin */
1699                 VectorCopy( sampleOrigin, origin[ b ] );
1700
1701                 /* calculate position */
1702                 if ( !SubmapRawLuxel( lm, x, y, ( bias * biasDirs[ b ][ 0 ] ), ( bias * biasDirs[ b ][ 1 ] ), &cluster[ b ], origin[ b ], normal[ b ] ) ) {
1703                         cluster[ b ] = -1;
1704                         continue;
1705                 }
1706                 mapped++;
1707
1708                 /* increment sample count */
1709                 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;
1710
1711                 /* setup trace */
1712                 trace->cluster = *cluster;
1713                 VectorCopy( origin[ b ], trace->origin );
1714                 VectorCopy( normal[ b ], trace->normal );
1715
1716                 /* sample light */
1717
1718                 LightContributionToSample( trace );
1719
1720                 /* add to totals (fixme: make contrast function) */
1721                 VectorCopy( trace->color, luxel[ b ] );
1722                 VectorAdd( total, trace->color, total );
1723                 if ( ( luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ] ) > 0.0f ) {
1724                         lighted++;
1725                 }
1726         }
1727
1728         /* subsample further? */
1729         if ( ( lightLuxel[ 3 ] + 1.0f ) < lightSamples &&
1730                  ( total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f ) &&
1731                  lighted != 0 && lighted != mapped ) {
1732                 for ( b = 0; b < 4; b++ )
1733                 {
1734                         if ( cluster[ b ] < 0 ) {
1735                                 continue;
1736                         }
1737                         SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, ( bias * 0.25f ), luxel[ b ] );
1738                 }
1739         }
1740
1741         /* average */
1742         //%     VectorClear( color );
1743         //%     samples = 0;
1744         VectorCopy( lightLuxel, color );
1745         samples = 1;
1746         for ( b = 0; b < 4; b++ )
1747         {
1748                 if ( cluster[ b ] < 0 ) {
1749                         continue;
1750                 }
1751                 VectorAdd( color, luxel[ b ], color );
1752                 samples++;
1753         }
1754
1755         /* add to luxel */
1756         if ( samples > 0 ) {
1757                 /* average */
1758                 color[ 0 ] /= samples;
1759                 color[ 1 ] /= samples;
1760                 color[ 2 ] /= samples;
1761
1762                 /* add to color */
1763                 VectorCopy( color, lightLuxel );
1764                 lightLuxel[ 3 ] += 1.0f;
1765         }
1766 }
1767
1768
1769
1770 /*
1771    IlluminateRawLightmap()
1772    illuminates the luxels
1773  */
1774
1775 #define STACK_LL_SIZE           ( SUPER_LUXEL_SIZE * 64 * 64 )
1776 #define LIGHT_LUXEL( x, y )     ( lightLuxels + ( ( ( ( y ) * lm->sw ) + ( x ) ) * SUPER_LUXEL_SIZE ) )
1777
1778 void IlluminateRawLightmap( int rawLightmapNum ){
1779         int i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;
1780         int                 *cluster, *cluster2, mapped, lighted, totalLighted;
1781         rawLightmap_t       *lm;
1782         surfaceInfo_t       *info;
1783         qboolean filterColor, filterDir;
1784         float brightness;
1785         float               *origin, *normal, *dirt, *luxel, *luxel2, *deluxel, *deluxel2;
1786         float               *lightLuxels, *lightLuxel, samples, filterRadius, weight;
1787         vec3_t color, averageColor, averageDir, total, temp, temp2;
1788         float tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
1789         trace_t trace;
1790         float stackLightLuxels[ STACK_LL_SIZE ];
1791
1792
1793         /* bail if this number exceeds the number of raw lightmaps */
1794         if ( rawLightmapNum >= numRawLightmaps ) {
1795                 return;
1796         }
1797
1798         /* get lightmap */
1799         lm = &rawLightmaps[ rawLightmapNum ];
1800
1801         /* setup trace */
1802         trace.testOcclusion = !noTrace;
1803         trace.forceSunlight = qfalse;
1804         trace.recvShadows = lm->recvShadows;
1805         trace.numSurfaces = lm->numLightSurfaces;
1806         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];
1807         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
1808
1809         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */
1810         trace.twoSided = qfalse;
1811         for ( i = 0; i < trace.numSurfaces; i++ )
1812         {
1813                 /* get surface */
1814                 info = &surfaceInfos[ trace.surfaces[ i ] ];
1815
1816                 /* check twosidedness */
1817                 if ( info->si->twoSided ) {
1818                         trace.twoSided = qtrue;
1819                         break;
1820                 }
1821         }
1822
1823         /* create a culled light list for this raw lightmap */
1824         CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );
1825
1826         /* -----------------------------------------------------------------
1827            fill pass
1828            ----------------------------------------------------------------- */
1829
1830         /* set counts */
1831         numLuxelsIlluminated += ( lm->sw * lm->sh );
1832
1833         /* test debugging state */
1834         if ( debugSurfaces || debugAxis || debugCluster || debugOrigin || dirtDebug || normalmap ) {
1835                 /* debug fill the luxels */
1836                 for ( y = 0; y < lm->sh; y++ )
1837                 {
1838                         for ( x = 0; x < lm->sw; x++ )
1839                         {
1840                                 /* get cluster */
1841                                 cluster = SUPER_CLUSTER( x, y );
1842
1843                                 /* only fill mapped luxels */
1844                                 if ( *cluster < 0 ) {
1845                                         continue;
1846                                 }
1847
1848                                 /* get particulars */
1849                                 luxel = SUPER_LUXEL( 0, x, y );
1850                                 origin = SUPER_ORIGIN( x, y );
1851                                 normal = SUPER_NORMAL( x, y );
1852
1853                                 /* color the luxel with raw lightmap num? */
1854                                 if ( debugSurfaces ) {
1855                                         VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );
1856                                 }
1857
1858                                 /* color the luxel with lightmap axis? */
1859                                 else if ( debugAxis ) {
1860                                         luxel[ 0 ] = ( lm->axis[ 0 ] + 1.0f ) * 127.5f;
1861                                         luxel[ 1 ] = ( lm->axis[ 1 ] + 1.0f ) * 127.5f;
1862                                         luxel[ 2 ] = ( lm->axis[ 2 ] + 1.0f ) * 127.5f;
1863                                 }
1864
1865                                 /* color the luxel with luxel cluster? */
1866                                 else if ( debugCluster ) {
1867                                         VectorCopy( debugColors[ *cluster % 12 ], luxel );
1868                                 }
1869
1870                                 /* color the luxel with luxel origin? */
1871                                 else if ( debugOrigin ) {
1872                                         VectorSubtract( lm->maxs, lm->mins, temp );
1873                                         VectorScale( temp, ( 1.0f / 255.0f ), temp );
1874                                         VectorSubtract( origin, lm->mins, temp2 );
1875                                         luxel[ 0 ] = lm->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
1876                                         luxel[ 1 ] = lm->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
1877                                         luxel[ 2 ] = lm->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
1878                                 }
1879
1880                                 /* color the luxel with the normal */
1881                                 else if ( normalmap ) {
1882                                         luxel[ 0 ] = ( normal[ 0 ] + 1.0f ) * 127.5f;
1883                                         luxel[ 1 ] = ( normal[ 1 ] + 1.0f ) * 127.5f;
1884                                         luxel[ 2 ] = ( normal[ 2 ] + 1.0f ) * 127.5f;
1885                                 }
1886
1887                                 /* otherwise clear it */
1888                                 else{
1889                                         VectorClear( luxel );
1890                                 }
1891
1892                                 /* add to counts */
1893                                 luxel[ 3 ] = 1.0f;
1894                         }
1895                 }
1896         }
1897         else
1898         {
1899                 /* allocate temporary per-light luxel storage */
1900                 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1901                 if ( llSize <= ( STACK_LL_SIZE * sizeof( float ) ) ) {
1902                         lightLuxels = stackLightLuxels;
1903                 }
1904                 else{
1905                         lightLuxels = safe_malloc( llSize );
1906                 }
1907
1908                 /* clear luxels */
1909                 //%     memset( lm->superLuxels[ 0 ], 0, llSize );
1910
1911                 /* set ambient color */
1912                 for ( y = 0; y < lm->sh; y++ )
1913                 {
1914                         for ( x = 0; x < lm->sw; x++ )
1915                         {
1916                                 /* get cluster */
1917                                 cluster = SUPER_CLUSTER( x, y );
1918                                 luxel = SUPER_LUXEL( 0, x, y );
1919                                 normal = SUPER_NORMAL( x, y );
1920                                 deluxel = SUPER_DELUXEL( x, y );
1921
1922                                 /* blacken unmapped clusters */
1923                                 if ( *cluster < 0 ) {
1924                                         VectorClear( luxel );
1925                                 }
1926
1927                                 /* set ambient */
1928                                 else
1929                                 {
1930                                         VectorCopy( ambientColor, luxel );
1931                                         if ( deluxemap ) {
1932                                                 VectorScale( normal, 0.00390625f, deluxel );
1933                                         }
1934                                         luxel[ 3 ] = 1.0f;
1935                                 }
1936                         }
1937                 }
1938
1939                 /* clear styled lightmaps */
1940                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
1941                 for ( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1942                 {
1943                         if ( lm->superLuxels[ lightmapNum ] != NULL ) {
1944                                 memset( lm->superLuxels[ lightmapNum ], 0, size );
1945                         }
1946                 }
1947
1948                 /* debugging code */
1949                 //%     if( trace.numLights <= 0 )
1950                 //%             Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );
1951
1952                 /* walk light list */
1953                 for ( i = 0; i < trace.numLights; i++ )
1954                 {
1955                         /* setup trace */
1956                         trace.light = trace.lights[ i ];
1957
1958                         /* style check */
1959                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
1960                         {
1961                                 if ( lm->styles[ lightmapNum ] == trace.light->style ||
1962                                          lm->styles[ lightmapNum ] == LS_NONE ) {
1963                                         break;
1964                                 }
1965                         }
1966
1967                         /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */
1968                         if ( lightmapNum >= MAX_LIGHTMAPS ) {
1969                                 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );
1970                                 continue;
1971                         }
1972
1973                         /* setup */
1974                         memset( lightLuxels, 0, llSize );
1975                         totalLighted = 0;
1976
1977                         /* initial pass, one sample per luxel */
1978                         for ( y = 0; y < lm->sh; y++ )
1979                         {
1980                                 for ( x = 0; x < lm->sw; x++ )
1981                                 {
1982                                         /* get cluster */
1983                                         cluster = SUPER_CLUSTER( x, y );
1984                                         if ( *cluster < 0 ) {
1985                                                 continue;
1986                                         }
1987
1988                                         /* get particulars */
1989                                         lightLuxel = LIGHT_LUXEL( x, y );
1990                                         deluxel = SUPER_DELUXEL( x, y );
1991                                         origin = SUPER_ORIGIN( x, y );
1992                                         normal = SUPER_NORMAL( x, y );
1993
1994                                         /* set contribution count */
1995                                         lightLuxel[ 3 ] = 1.0f;
1996
1997                                         /* setup trace */
1998                                         trace.cluster = *cluster;
1999                                         VectorCopy( origin, trace.origin );
2000                                         VectorCopy( normal, trace.normal );
2001
2002                                         /* get light for this sample */
2003                                         LightContributionToSample( &trace );
2004                                         VectorCopy( trace.color, lightLuxel );
2005
2006                                         /* add to count */
2007                                         if ( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] ) {
2008                                                 totalLighted++;
2009                                         }
2010
2011                                         /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */
2012                                         if ( deluxemap ) {
2013                                                 /* color to grayscale (photoshop rgb weighting) */
2014                                                 brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;
2015                                                 brightness *= ( 1.0 / 255.0 );
2016                                                 VectorScale( trace.direction, brightness, trace.direction );
2017                                                 VectorAdd( deluxel, trace.direction, deluxel );
2018                                         }
2019                                 }
2020                         }
2021
2022                         /* don't even bother with everything else if nothing was lit */
2023                         if ( totalLighted == 0 ) {
2024                                 continue;
2025                         }
2026
2027                         /* determine filter radius */
2028                         filterRadius = lm->filterRadius > trace.light->filterRadius
2029                                                    ? lm->filterRadius
2030                                                    : trace.light->filterRadius;
2031                         if ( filterRadius < 0.0f ) {
2032                                 filterRadius = 0.0f;
2033                         }
2034
2035                         /* set luxel filter radius */
2036                         luxelFilterRadius = superSample * filterRadius / lm->sampleSize;
2037                         if ( luxelFilterRadius == 0 && ( filterRadius > 0.0f || filter ) ) {
2038                                 luxelFilterRadius = 1;
2039                         }
2040
2041                         /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */
2042                         /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */
2043                         if ( lightSamples > 1 && luxelFilterRadius == 0 ) {
2044                                 /* walk luxels */
2045                                 for ( y = 0; y < ( lm->sh - 1 ); y++ )
2046                                 {
2047                                         for ( x = 0; x < ( lm->sw - 1 ); x++ )
2048                                         {
2049                                                 /* setup */
2050                                                 mapped = 0;
2051                                                 lighted = 0;
2052                                                 VectorClear( total );
2053
2054                                                 /* test 2x2 stamp */
2055                                                 for ( t = 0; t < 4; t++ )
2056                                                 {
2057                                                         /* set sample coords */
2058                                                         sx = x + tests[ t ][ 0 ];
2059                                                         sy = y + tests[ t ][ 1 ];
2060
2061                                                         /* get cluster */
2062                                                         cluster = SUPER_CLUSTER( sx, sy );
2063                                                         if ( *cluster < 0 ) {
2064                                                                 continue;
2065                                                         }
2066                                                         mapped++;
2067
2068                                                         /* get luxel */
2069                                                         lightLuxel = LIGHT_LUXEL( sx, sy );
2070                                                         VectorAdd( total, lightLuxel, total );
2071                                                         if ( ( lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ] ) > 0.0f ) {
2072                                                                 lighted++;
2073                                                         }
2074                                                 }
2075
2076                                                 /* if total color is under a certain amount, then don't bother subsampling */
2077                                                 if ( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f ) {
2078                                                         continue;
2079                                                 }
2080
2081                                                 /* if all 4 pixels are either in shadow or light, then don't subsample */
2082                                                 if ( lighted != 0 && lighted != mapped ) {
2083                                                         for ( t = 0; t < 4; t++ )
2084                                                         {
2085                                                                 /* set sample coords */
2086                                                                 sx = x + tests[ t ][ 0 ];
2087                                                                 sy = y + tests[ t ][ 1 ];
2088
2089                                                                 /* get luxel */
2090                                                                 cluster = SUPER_CLUSTER( sx, sy );
2091                                                                 if ( *cluster < 0 ) {
2092                                                                         continue;
2093                                                                 }
2094                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );
2095                                                                 origin = SUPER_ORIGIN( sx, sy );
2096
2097                                                                 /* only subsample shadowed luxels */
2098                                                                 //%     if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )
2099                                                                 //%             continue;
2100
2101                                                                 /* subsample it */
2102                                                                 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );
2103
2104                                                                 /* debug code to colorize subsampled areas to yellow */
2105                                                                 //%     luxel = SUPER_LUXEL( lightmapNum, sx, sy );
2106                                                                 //%     VectorSet( luxel, 255, 204, 0 );
2107                                                         }
2108                                                 }
2109                                         }
2110                                 }
2111                         }
2112
2113                         /* tertiary pass, apply dirt map (ambient occlusion) */
2114                         if ( 0 && dirty ) {
2115                                 /* walk luxels */
2116                                 for ( y = 0; y < lm->sh; y++ )
2117                                 {
2118                                         for ( x = 0; x < lm->sw; x++ )
2119                                         {
2120                                                 /* get cluster  */
2121                                                 cluster = SUPER_CLUSTER( x, y );
2122                                                 if ( *cluster < 0 ) {
2123                                                         continue;
2124                                                 }
2125
2126                                                 /* get particulars */
2127                                                 lightLuxel = LIGHT_LUXEL( x, y );
2128                                                 dirt = SUPER_DIRT( x, y );
2129
2130                                                 /* scale light value */
2131                                                 VectorScale( lightLuxel, *dirt, lightLuxel );
2132                                         }
2133                                 }
2134                         }
2135
2136                         /* allocate sampling lightmap storage */
2137                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2138                                 /* allocate sampling lightmap storage */
2139                                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );
2140                                 lm->superLuxels[ lightmapNum ] = safe_malloc( size );
2141                                 memset( lm->superLuxels[ lightmapNum ], 0, size );
2142                         }
2143
2144                         /* set style */
2145                         if ( lightmapNum > 0 ) {
2146                                 lm->styles[ lightmapNum ] = trace.light->style;
2147                                 //%     Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );
2148                         }
2149
2150                         /* copy to permanent luxels */
2151                         for ( y = 0; y < lm->sh; y++ )
2152                         {
2153                                 for ( x = 0; x < lm->sw; x++ )
2154                                 {
2155                                         /* get cluster and origin */
2156                                         cluster = SUPER_CLUSTER( x, y );
2157                                         if ( *cluster < 0 ) {
2158                                                 continue;
2159                                         }
2160                                         origin = SUPER_ORIGIN( x, y );
2161
2162                                         /* filter? */
2163                                         if ( luxelFilterRadius ) {
2164                                                 /* setup */
2165                                                 VectorClear( averageColor );
2166                                                 samples = 0.0f;
2167
2168                                                 /* cheaper distance-based filtering */
2169                                                 for ( sy = ( y - luxelFilterRadius ); sy <= ( y + luxelFilterRadius ); sy++ )
2170                                                 {
2171                                                         if ( sy < 0 || sy >= lm->sh ) {
2172                                                                 continue;
2173                                                         }
2174
2175                                                         for ( sx = ( x - luxelFilterRadius ); sx <= ( x + luxelFilterRadius ); sx++ )
2176                                                         {
2177                                                                 if ( sx < 0 || sx >= lm->sw ) {
2178                                                                         continue;
2179                                                                 }
2180
2181                                                                 /* get particulars */
2182                                                                 cluster = SUPER_CLUSTER( sx, sy );
2183                                                                 if ( *cluster < 0 ) {
2184                                                                         continue;
2185                                                                 }
2186                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );
2187
2188                                                                 /* create weight */
2189                                                                 weight = ( abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f );
2190                                                                 weight *= ( abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f );
2191
2192                                                                 /* scale luxel by filter weight */
2193                                                                 VectorScale( lightLuxel, weight, color );
2194                                                                 VectorAdd( averageColor, color, averageColor );
2195                                                                 samples += weight;
2196                                                         }
2197                                                 }
2198
2199                                                 /* any samples? */
2200                                                 if ( samples <= 0.0f ) {
2201                                                         continue;
2202                                                 }
2203
2204                                                 /* scale into luxel */
2205                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2206                                                 luxel[ 3 ] = 1.0f;
2207
2208                                                 /* handle negative light */
2209                                                 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2210                                                         luxel[ 0 ] -= averageColor[ 0 ] / samples;
2211                                                         luxel[ 1 ] -= averageColor[ 1 ] / samples;
2212                                                         luxel[ 2 ] -= averageColor[ 2 ] / samples;
2213                                                 }
2214
2215                                                 /* handle normal light */
2216                                                 else
2217                                                 {
2218                                                         luxel[ 0 ] += averageColor[ 0 ] / samples;
2219                                                         luxel[ 1 ] += averageColor[ 1 ] / samples;
2220                                                         luxel[ 2 ] += averageColor[ 2 ] / samples;
2221                                                 }
2222                                         }
2223
2224                                         /* single sample */
2225                                         else
2226                                         {
2227                                                 /* get particulars */
2228                                                 lightLuxel = LIGHT_LUXEL( x, y );
2229                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2230
2231                                                 /* handle negative light */
2232                                                 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2233                                                         VectorScale( averageColor, -1.0f, averageColor );
2234                                                 }
2235
2236                                                 /* add color */
2237                                                 luxel[ 3 ] = 1.0f;
2238
2239                                                 /* handle negative light */
2240                                                 if ( trace.light->flags & LIGHT_NEGATIVE ) {
2241                                                         VectorSubtract( luxel, lightLuxel, luxel );
2242                                                 }
2243
2244                                                 /* handle normal light */
2245                                                 else{
2246                                                         VectorAdd( luxel, lightLuxel, luxel );
2247                                                 }
2248                                         }
2249                                 }
2250                         }
2251                 }
2252
2253                 /* free temporary luxels */
2254                 if ( lightLuxels != stackLightLuxels ) {
2255                         free( lightLuxels );
2256                 }
2257         }
2258
2259         /* free light list */
2260         FreeTraceLights( &trace );
2261
2262         /*      -----------------------------------------------------------------
2263             dirt pass
2264             ----------------------------------------------------------------- */
2265
2266         if ( dirty ) {
2267                 /* walk lightmaps */
2268                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2269                 {
2270                         /* early out */
2271                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2272                                 continue;
2273                         }
2274
2275                         /* apply dirt to each luxel */
2276                         for ( y = 0; y < lm->sh; y++ )
2277                         {
2278                                 for ( x = 0; x < lm->sw; x++ )
2279                                 {
2280                                         /* get cluster */
2281                                         cluster = SUPER_CLUSTER( x, y );
2282                                         //%     if( *cluster < 0 )
2283                                         //%             continue;
2284
2285                                         /* get particulars */
2286                                         luxel = SUPER_LUXEL( lightmapNum, x, y );
2287                                         dirt = SUPER_DIRT( x, y );
2288
2289                                         /* apply dirt */
2290                                         VectorScale( luxel, *dirt, luxel );
2291
2292                                         /* debugging */
2293                                         if ( dirtDebug ) {
2294                                                 VectorSet( luxel, *dirt * 255.0f, *dirt * 255.0f, *dirt * 255.0f );
2295                                         }
2296                                 }
2297                         }
2298                 }
2299         }
2300
2301         /* -----------------------------------------------------------------
2302            filter pass
2303            ----------------------------------------------------------------- */
2304
2305         /* walk lightmaps */
2306         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2307         {
2308                 /* early out */
2309                 if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2310                         continue;
2311                 }
2312
2313                 /* average occluded luxels from neighbors */
2314                 for ( y = 0; y < lm->sh; y++ )
2315                 {
2316                         for ( x = 0; x < lm->sw; x++ )
2317                         {
2318                                 /* get particulars */
2319                                 cluster = SUPER_CLUSTER( x, y );
2320                                 luxel = SUPER_LUXEL( lightmapNum, x, y );
2321                                 deluxel = SUPER_DELUXEL( x, y );
2322                                 normal = SUPER_NORMAL( x, y );
2323
2324                                 /* determine if filtering is necessary */
2325                                 filterColor = qfalse;
2326                                 filterDir = qfalse;
2327                                 if ( *cluster < 0 ||
2328                                          ( lm->splotchFix && ( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] ) ) ) {
2329                                         filterColor = qtrue;
2330                                 }
2331                                 if ( deluxemap && lightmapNum == 0 && ( *cluster < 0 || filter ) ) {
2332                                         filterDir = qtrue;
2333                                 }
2334
2335                                 if ( !filterColor && !filterDir ) {
2336                                         continue;
2337                                 }
2338
2339                                 /* choose seed amount */
2340                                 VectorClear( averageColor );
2341                                 VectorClear( averageDir );
2342                                 samples = 0.0f;
2343
2344                                 /* walk 3x3 matrix */
2345                                 for ( sy = ( y - 1 ); sy <= ( y + 1 ); sy++ )
2346                                 {
2347                                         if ( sy < 0 || sy >= lm->sh ) {
2348                                                 continue;
2349                                         }
2350
2351                                         for ( sx = ( x - 1 ); sx <= ( x + 1 ); sx++ )
2352                                         {
2353                                                 if ( sx < 0 || sx >= lm->sw || ( sx == x && sy == y ) ) {
2354                                                         continue;
2355                                                 }
2356
2357                                                 /* get neighbor's particulars */
2358                                                 cluster2 = SUPER_CLUSTER( sx, sy );
2359                                                 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );
2360                                                 deluxel2 = SUPER_DELUXEL( sx, sy );
2361
2362                                                 /* ignore unmapped/unlit luxels */
2363                                                 if ( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||
2364                                                          ( lm->splotchFix && VectorCompare( luxel2, ambientColor ) ) ) {
2365                                                         continue;
2366                                                 }
2367
2368                                                 /* add its distinctiveness to our own */
2369                                                 VectorAdd( averageColor, luxel2, averageColor );
2370                                                 samples += luxel2[ 3 ];
2371                                                 if ( filterDir ) {
2372                                                         VectorAdd( averageDir, deluxel2, averageDir );
2373                                                 }
2374                                         }
2375                                 }
2376
2377                                 /* fall through */
2378                                 if ( samples <= 0.0f ) {
2379                                         continue;
2380                                 }
2381
2382                                 /* dark lightmap seams */
2383                                 if ( dark ) {
2384                                         if ( lightmapNum == 0 ) {
2385                                                 VectorMA( averageColor, 2.0f, ambientColor, averageColor );
2386                                         }
2387                                         samples += 2.0f;
2388                                 }
2389
2390                                 /* average it */
2391                                 if ( filterColor ) {
2392                                         VectorDivide( averageColor, samples, luxel );
2393                                         luxel[ 3 ] = 1.0f;
2394                                 }
2395                                 if ( filterDir ) {
2396                                         VectorDivide( averageDir, samples, deluxel );
2397                                 }
2398
2399                                 /* set cluster to -3 */
2400                                 if ( *cluster < 0 ) {
2401                                         *cluster = CLUSTER_FLOODED;
2402                                 }
2403                         }
2404                 }
2405         }
2406 }
2407
2408
2409
2410 /*
2411    IlluminateVertexes()
2412    light the surface vertexes
2413  */
2414
2415 #define VERTEX_NUDGE    4.0f
2416
2417 void IlluminateVertexes( int num ){
2418         int i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;
2419         int lightmapNum, numAvg;
2420         float samples, *vertLuxel, *radVertLuxel, *luxel, dirt;
2421         vec3_t origin, temp, temp2, colors[ MAX_LIGHTMAPS ], avgColors[ MAX_LIGHTMAPS ];
2422         bspDrawSurface_t    *ds;
2423         surfaceInfo_t       *info;
2424         rawLightmap_t       *lm;
2425         bspDrawVert_t       *verts;
2426         trace_t trace;
2427
2428
2429         /* get surface, info, and raw lightmap */
2430         ds = &bspDrawSurfaces[ num ];
2431         info = &surfaceInfos[ num ];
2432         lm = info->lm;
2433
2434         /* -----------------------------------------------------------------
2435            illuminate the vertexes
2436            ----------------------------------------------------------------- */
2437
2438         /* calculate vertex lighting for surfaces without lightmaps */
2439         if ( lm == NULL || cpmaHack ) {
2440                 /* setup trace */
2441                 trace.testOcclusion = ( cpmaHack && lm != NULL ) ? qfalse : !noTrace;
2442                 trace.forceSunlight = info->si->forceSunlight;
2443                 trace.recvShadows = info->recvShadows;
2444                 trace.numSurfaces = 1;
2445                 trace.surfaces = &num;
2446                 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;
2447
2448                 /* twosided lighting */
2449                 trace.twoSided = info->si->twoSided;
2450
2451                 /* make light list for this surface */
2452                 CreateTraceLightsForSurface( num, &trace );
2453
2454                 /* setup */
2455                 verts = yDrawVerts + ds->firstVert;
2456                 numAvg = 0;
2457                 memset( avgColors, 0, sizeof( avgColors ) );
2458
2459                 /* walk the surface verts */
2460                 for ( i = 0; i < ds->numVerts; i++ )
2461                 {
2462                         /* get vertex luxel */
2463                         radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2464
2465                         /* color the luxel with raw lightmap num? */
2466                         if ( debugSurfaces ) {
2467                                 VectorCopy( debugColors[ num % 12 ], radVertLuxel );
2468                         }
2469
2470                         /* color the luxel with luxel origin? */
2471                         else if ( debugOrigin ) {
2472                                 VectorSubtract( info->maxs, info->mins, temp );
2473                                 VectorScale( temp, ( 1.0f / 255.0f ), temp );
2474                                 VectorSubtract( origin, lm->mins, temp2 );
2475                                 radVertLuxel[ 0 ] = info->mins[ 0 ] + ( temp[ 0 ] * temp2[ 0 ] );
2476                                 radVertLuxel[ 1 ] = info->mins[ 1 ] + ( temp[ 1 ] * temp2[ 1 ] );
2477                                 radVertLuxel[ 2 ] = info->mins[ 2 ] + ( temp[ 2 ] * temp2[ 2 ] );
2478                         }
2479
2480                         /* color the luxel with the normal */
2481                         else if ( normalmap ) {
2482                                 radVertLuxel[ 0 ] = ( verts[ i ].normal[ 0 ] + 1.0f ) * 127.5f;
2483                                 radVertLuxel[ 1 ] = ( verts[ i ].normal[ 1 ] + 1.0f ) * 127.5f;
2484                                 radVertLuxel[ 2 ] = ( verts[ i ].normal[ 2 ] + 1.0f ) * 127.5f;
2485                         }
2486
2487                         /* illuminate the vertex */
2488                         else
2489                         {
2490                                 /* clear vertex luxel */
2491                                 VectorSet( radVertLuxel, -1.0f, -1.0f, -1.0f );
2492
2493                                 /* try at initial origin */
2494                                 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2495                                 if ( trace.cluster >= 0 ) {
2496                                         /* setup trace */
2497                                         VectorCopy( verts[ i ].xyz, trace.origin );
2498                                         VectorCopy( verts[ i ].normal, trace.normal );
2499
2500                                         /* r7 dirt */
2501                                         if ( dirty ) {
2502                                                 dirt = DirtForSample( &trace );
2503                                         }
2504                                         else{
2505                                                 dirt = 1.0f;
2506                                         }
2507
2508                                         /* trace */
2509                                         LightingAtSample( &trace, ds->vertexStyles, colors );
2510
2511                                         /* store */
2512                                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2513                                         {
2514                                                 /* r7 dirt */
2515                                                 VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2516
2517                                                 /* store */
2518                                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2519                                                 VectorCopy( colors[ lightmapNum ], radVertLuxel );
2520                                                 VectorAdd( avgColors[ lightmapNum ], colors[ lightmapNum ], colors[ lightmapNum ] );
2521                                         }
2522                                 }
2523
2524                                 /* is this sample bright enough? */
2525                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2526                                 if ( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&
2527                                          radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&
2528                                          radVertLuxel[ 2 ] <= ambientColor[ 2 ] ) {
2529                                         /* nudge the sample point around a bit */
2530                                         for ( x = 0; x < 4; x++ )
2531                                         {
2532                                                 /* two's complement 0, 1, -1, 2, -2, etc */
2533                                                 x1 = ( ( x >> 1 ) ^ ( x & 1 ? -1 : 0 ) ) + ( x & 1 );
2534
2535                                                 for ( y = 0; y < 4; y++ )
2536                                                 {
2537                                                         y1 = ( ( y >> 1 ) ^ ( y & 1 ? -1 : 0 ) ) + ( y & 1 );
2538
2539                                                         for ( z = 0; z < 4; z++ )
2540                                                         {
2541                                                                 z1 = ( ( z >> 1 ) ^ ( z & 1 ? -1 : 0 ) ) + ( z & 1 );
2542
2543                                                                 /* nudge origin */
2544                                                                 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + ( VERTEX_NUDGE * x1 );
2545                                                                 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + ( VERTEX_NUDGE * y1 );
2546                                                                 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + ( VERTEX_NUDGE * z1 );
2547
2548                                                                 /* try at nudged origin */
2549                                                                 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );
2550                                                                 if ( trace.cluster < 0 ) {
2551                                                                         continue;
2552                                                                 }
2553
2554                                                                 /* trace */
2555                                                                 LightingAtSample( &trace, ds->vertexStyles, colors );
2556
2557                                                                 /* store */
2558                                                                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2559                                                                 {
2560                                                                         /* r7 dirt */
2561                                                                         VectorScale( colors[ lightmapNum ], dirt, colors[ lightmapNum ] );
2562
2563                                                                         /* store */
2564                                                                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2565                                                                         VectorCopy( colors[ lightmapNum ], radVertLuxel );
2566                                                                 }
2567
2568                                                                 /* bright enough? */
2569                                                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2570                                                                 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2571                                                                          radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2572                                                                          radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2573                                                                         x = y = z = 1000;
2574                                                                 }
2575                                                         }
2576                                                 }
2577                                         }
2578                                 }
2579
2580                                 /* add to average? */
2581                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2582                                 if ( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||
2583                                          radVertLuxel[ 1 ] > ambientColor[ 1 ] ||
2584                                          radVertLuxel[ 2 ] > ambientColor[ 2 ] ) {
2585                                         numAvg++;
2586                                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2587                                         {
2588                                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2589                                                 VectorAdd( avgColors[ lightmapNum ], radVertLuxel, avgColors[ lightmapNum ] );
2590                                         }
2591                                 }
2592                         }
2593
2594                         /* another happy customer */
2595                         numVertsIlluminated++;
2596                 }
2597
2598                 /* set average color */
2599                 if ( numAvg > 0 ) {
2600                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2601                                 VectorScale( avgColors[ lightmapNum ], ( 1.0f / numAvg ), avgColors[ lightmapNum ] );
2602                 }
2603                 else
2604                 {
2605                         VectorCopy( ambientColor, avgColors[ 0 ] );
2606                 }
2607
2608                 /* clean up and store vertex color */
2609                 for ( i = 0; i < ds->numVerts; i++ )
2610                 {
2611                         /* get vertex luxel */
2612                         radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );
2613
2614                         /* store average in occluded vertexes */
2615                         if ( radVertLuxel[ 0 ] < 0.0f ) {
2616                                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2617                                 {
2618                                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2619                                         VectorCopy( avgColors[ lightmapNum ], radVertLuxel );
2620
2621                                         /* debug code */
2622                                         //%     VectorSet( radVertLuxel, 255.0f, 0.0f, 0.0f );
2623                                 }
2624                         }
2625
2626                         /* store it */
2627                         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2628                         {
2629                                 /* get luxels */
2630                                 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2631                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2632
2633                                 /* store */
2634                                 if ( bouncing || bounce == 0 || !bounceOnly ) {
2635                                         VectorAdd( vertLuxel, radVertLuxel, vertLuxel );
2636                                 }
2637                                 if ( !info->si->noVertexLight ) {
2638                                         ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );
2639                                 }
2640                         }
2641                 }
2642
2643                 /* free light list */
2644                 FreeTraceLights( &trace );
2645
2646                 /* return to sender */
2647                 return;
2648         }
2649
2650         /* -----------------------------------------------------------------
2651            reconstitute vertex lighting from the luxels
2652            ----------------------------------------------------------------- */
2653
2654         /* set styles from lightmap */
2655         for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2656                 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];
2657
2658         /* get max search radius */
2659         maxRadius = lm->sw;
2660         maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;
2661
2662         /* walk the surface verts */
2663         verts = yDrawVerts + ds->firstVert;
2664         for ( i = 0; i < ds->numVerts; i++ )
2665         {
2666                 /* do each lightmap */
2667                 for ( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )
2668                 {
2669                         /* early out */
2670                         if ( lm->superLuxels[ lightmapNum ] == NULL ) {
2671                                 continue;
2672                         }
2673
2674                         /* get luxel coords */
2675                         x = verts[ i ].lightmap[ lightmapNum ][ 0 ];
2676                         y = verts[ i ].lightmap[ lightmapNum ][ 1 ];
2677                         if ( x < 0 ) {
2678                                 x = 0;
2679                         }
2680                         else if ( x >= lm->sw ) {
2681                                 x = lm->sw - 1;
2682                         }
2683                         if ( y < 0 ) {
2684                                 y = 0;
2685                         }
2686                         else if ( y >= lm->sh ) {
2687                                 y = lm->sh - 1;
2688                         }
2689
2690                         /* get vertex luxels */
2691                         vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2692                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );
2693
2694                         /* color the luxel with the normal? */
2695                         if ( normalmap ) {