]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/light_ydnar.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3map2 / light_ydnar.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 \r
21 ----------------------------------------------------------------------------------\r
22 \r
23 This code has been altered significantly from its original form, to support\r
24 several games based on the Quake III Arena engine, in the form of "Q3Map2."\r
25 \r
26 ------------------------------------------------------------------------------- */\r
27 \r
28 \r
29 \r
30 /* marker */\r
31 #define LIGHT_YDNAR_C\r
32 \r
33 \r
34 \r
35 /* dependencies */\r
36 #include "q3map2.h"\r
37 \r
38 \r
39 \r
40 \r
41 /*\r
42 ColorToBytes()\r
43 ydnar: moved to here 2001-02-04\r
44 */\r
45 \r
46 void ColorToBytes( const float *color, byte *colorBytes, float scale )\r
47 {\r
48         float   max;\r
49         vec3_t  sample;\r
50         \r
51         \r
52         /* ydnar: scaling necessary for simulating r_overbrightBits on external lightmaps */\r
53         if( scale <= 0.0f )\r
54                 scale = 1.0f;\r
55         \r
56         /* make a local copy */\r
57         VectorScale( color, scale, sample );\r
58         \r
59         /* handle negative light */\r
60         if( sample[ 0 ] < 0.0f )\r
61                 sample[ 0 ] = 0.0f;\r
62         if( sample[ 1 ] < 0.0f )\r
63                 sample[ 1 ] = 0.0f;\r
64         if( sample[ 2 ] < 0.0f )\r
65                 sample[ 2 ] = 0.0f;\r
66         \r
67         /* clamp with color normalization */\r
68         max = sample[ 0 ];\r
69         if( sample[ 1 ] > max )\r
70                 max = sample[ 1 ];\r
71         if( sample[ 2 ] > max )\r
72                 max = sample[ 2 ];\r
73         if( max > 255.0f )\r
74                 VectorScale( sample, (255.0f / max), sample );\r
75         \r
76         /* store it off */\r
77         colorBytes[ 0 ] = sample[ 0 ];\r
78         colorBytes[ 1 ] = sample[ 1 ];\r
79         colorBytes[ 2 ] = sample[ 2 ];\r
80 }\r
81 \r
82 \r
83 \r
84 /* -------------------------------------------------------------------------------\r
85 \r
86 this section deals with phong shading (normal interpolation across brush faces)\r
87 \r
88 ------------------------------------------------------------------------------- */\r
89 \r
90 /*\r
91 SmoothNormals()\r
92 smooths together coincident vertex normals across the bsp\r
93 */\r
94 \r
95 #define MAX_SAMPLES                             256\r
96 #define THETA_EPSILON                   0.000001\r
97 #define EQUAL_NORMAL_EPSILON    0.01\r
98 \r
99 void SmoothNormals( void )\r
100 {\r
101         int                                     i, j, k, f, cs, numVerts, numVotes, fOld, start;\r
102         float                           shadeAngle, defaultShadeAngle, maxShadeAngle, dot, testAngle;\r
103         bspDrawSurface_t        *ds;\r
104         shaderInfo_t            *si;\r
105         float                           *shadeAngles;\r
106         byte                            *smoothed;\r
107         vec3_t                          average, diff;\r
108         int                                     indexes[ MAX_SAMPLES ];\r
109         vec3_t                          votes[ MAX_SAMPLES ];\r
110         \r
111         \r
112         /* allocate shade angle table */\r
113         shadeAngles = safe_malloc( numBSPDrawVerts * sizeof( float ) );\r
114         memset( shadeAngles, 0, numBSPDrawVerts * sizeof( float ) );\r
115         \r
116         /* allocate smoothed table */\r
117         cs = (numBSPDrawVerts / 8) + 1;\r
118         smoothed = safe_malloc( cs );\r
119         memset( smoothed, 0, cs );\r
120         \r
121         /* set default shade angle */\r
122         defaultShadeAngle = DEG2RAD( shadeAngleDegrees );\r
123         maxShadeAngle = 0;\r
124         \r
125         /* run through every surface and flag verts belonging to non-lightmapped surfaces\r
126            and set per-vertex smoothing angle */\r
127         for( i = 0; i < numBSPDrawSurfaces; i++ )\r
128         {\r
129                 /* get drawsurf */\r
130                 ds = &bspDrawSurfaces[ i ];\r
131                 \r
132                 /* get shader for shade angle */\r
133                 si = surfaceInfos[ i ].si;\r
134                 if( si->shadeAngleDegrees )\r
135                         shadeAngle = DEG2RAD( si->shadeAngleDegrees );\r
136                 else\r
137                         shadeAngle = defaultShadeAngle;\r
138                 if( shadeAngle > maxShadeAngle )\r
139                         maxShadeAngle = shadeAngle;\r
140                 \r
141                 /* flag its verts */\r
142                 for( j = 0; j < ds->numVerts; j++ )\r
143                 {\r
144                         f = ds->firstVert + j;\r
145                         shadeAngles[ f ] = shadeAngle;\r
146                         if( ds->surfaceType == MST_TRIANGLE_SOUP )\r
147                                 smoothed[ f >> 3 ] |= (1 << (f & 7));\r
148                 }\r
149                 \r
150                 /* ydnar: optional force-to-trisoup */\r
151                 if( trisoup && ds->surfaceType == MST_PLANAR )\r
152                 {\r
153                         ds->surfaceType = MST_TRIANGLE_SOUP;\r
154                         ds->lightmapNum[ 0 ] = -3;\r
155                 }\r
156         }\r
157         \r
158         /* bail if no surfaces have a shade angle */\r
159         if( maxShadeAngle == 0 )\r
160         {\r
161                 free( shadeAngles );\r
162                 free( smoothed );\r
163                 return;\r
164         }\r
165         \r
166         /* init pacifier */\r
167         fOld = -1;\r
168         start = I_FloatTime();\r
169         \r
170         /* go through the list of vertexes */\r
171         for( i = 0; i < numBSPDrawVerts; i++ )\r
172         {\r
173                 /* print pacifier */\r
174                 f = 10 * i / numBSPDrawVerts;\r
175                 if( f != fOld )\r
176                 {\r
177                         fOld = f;\r
178                         Sys_Printf( "%i...", f );\r
179                 }\r
180                 \r
181                 /* already smoothed? */\r
182                 if( smoothed[ i >> 3 ] & (1 << (i & 7)) )\r
183                         continue;\r
184                 \r
185                 /* clear */\r
186                 VectorClear( average );\r
187                 numVerts = 0;\r
188                 numVotes = 0;\r
189                 \r
190                 /* build a table of coincident vertexes */\r
191                 for( j = i; j < numBSPDrawVerts && numVerts < MAX_SAMPLES; j++ )\r
192                 {\r
193                         /* already smoothed? */\r
194                         if( smoothed[ j >> 3 ] & (1 << (j & 7)) )\r
195                                 continue;\r
196                         \r
197                         /* test vertexes */\r
198                         if( VectorCompare( yDrawVerts[ i ].xyz, yDrawVerts[ j ].xyz ) == qfalse )\r
199                                 continue;\r
200                         \r
201                         /* use smallest shade angle */\r
202                         shadeAngle = (shadeAngles[ i ] < shadeAngles[ j ] ? shadeAngles[ i ] : shadeAngles[ j ]);\r
203                         \r
204                         /* check shade angle */\r
205                         dot = DotProduct( bspDrawVerts[ i ].normal, bspDrawVerts[ j ].normal );\r
206                         if( dot > 1.0 )\r
207                                 dot = 1.0;\r
208                         else if( dot < -1.0 )\r
209                                 dot = -1.0;\r
210                         testAngle = acos( dot ) + THETA_EPSILON;\r
211                         if( testAngle >= shadeAngle )\r
212                         {\r
213                                 //Sys_Printf( "F(%3.3f >= %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );\r
214                                 continue;\r
215                         }\r
216                         //Sys_Printf( "P(%3.3f < %3.3f) ", RAD2DEG( testAngle ), RAD2DEG( shadeAngle ) );\r
217                         \r
218                         /* add to the list */\r
219                         indexes[ numVerts++ ] = j;\r
220                         \r
221                         /* flag vertex */\r
222                         smoothed[ j >> 3 ] |= (1 << (j & 7));\r
223                         \r
224                         /* see if this normal has already been voted */\r
225                         for( k = 0; k < numVotes; k++ )\r
226                         {\r
227                                 VectorSubtract( bspDrawVerts[ j ].normal, votes[ k ], diff );\r
228                                 if( fabs( diff[ 0 ] ) < EQUAL_NORMAL_EPSILON &&\r
229                                         fabs( diff[ 1 ] ) < EQUAL_NORMAL_EPSILON &&\r
230                                         fabs( diff[ 2 ] ) < EQUAL_NORMAL_EPSILON )\r
231                                         break;\r
232                         }\r
233                         \r
234                         /* add a new vote? */\r
235                         if( k == numVotes && numVotes < MAX_SAMPLES )\r
236                         {\r
237                                 VectorAdd( average, bspDrawVerts[ j ].normal, average );\r
238                                 VectorCopy( bspDrawVerts[ j ].normal, votes[ numVotes ] );\r
239                                 numVotes++;\r
240                         }\r
241                 }\r
242                 \r
243                 /* don't average for less than 2 verts */\r
244                 if( numVerts < 2 )\r
245                         continue;\r
246                 \r
247                 /* average normal */\r
248                 if( VectorNormalize( average, average ) > 0 )\r
249                 {\r
250                         /* smooth */\r
251                         for( j = 0; j < numVerts; j++ )\r
252                                 VectorCopy( average, yDrawVerts[ indexes[ j ] ].normal );\r
253                 }\r
254         }\r
255         \r
256         /* free the tables */\r
257         free( shadeAngles );\r
258         free( smoothed );\r
259         \r
260         /* print time */\r
261         Sys_Printf( " (%i)\n", (int) (I_FloatTime() - start) );\r
262 }\r
263 \r
264 \r
265 \r
266 /* -------------------------------------------------------------------------------\r
267 \r
268 this section deals with phong shaded lightmap tracing\r
269 \r
270 ------------------------------------------------------------------------------- */\r
271 \r
272 /* 9th rewrite (recursive subdivision of a lightmap triangle) */\r
273 \r
274 /*\r
275 CalcTangentVectors()\r
276 calculates the st tangent vectors for normalmapping\r
277 */\r
278 \r
279 static qboolean CalcTangentVectors( int numVerts, bspDrawVert_t **dv, vec3_t *stv, vec3_t *ttv )\r
280 {\r
281         int                     i;\r
282         float           bb, s, t;\r
283         vec3_t          bary;\r
284         \r
285         \r
286         /* calculate barycentric basis for the triangle */\r
287         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 ]);\r
288         if( fabs( bb ) < 0.00000001f )\r
289                 return qfalse;\r
290         \r
291         /* do each vertex */\r
292         for( i = 0; i < numVerts; i++ )\r
293         {\r
294                 /* calculate s tangent vector */\r
295                 s = dv[ i ]->st[ 0 ] + 10.0f;\r
296                 t = dv[ i ]->st[ 1 ];\r
297                 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;\r
298                 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;\r
299                 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;\r
300                 \r
301                 stv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];\r
302                 stv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];\r
303                 stv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];\r
304                 \r
305                 VectorSubtract( stv[ i ], dv[ i ]->xyz, stv[ i ] );\r
306                 VectorNormalize( stv[ i ], stv[ i ] );\r
307                 \r
308                 /* calculate t tangent vector */\r
309                 s = dv[ i ]->st[ 0 ];\r
310                 t = dv[ i ]->st[ 1 ] + 10.0f;\r
311                 bary[ 0 ] = ((dv[ 1 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t) - (dv[ 2 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t)) / bb;\r
312                 bary[ 1 ] = ((dv[ 2 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t) - (dv[ 0 ]->st[ 0 ] - s) * (dv[ 2 ]->st[ 1 ] - t)) / bb;\r
313                 bary[ 2 ] = ((dv[ 0 ]->st[ 0 ] - s) * (dv[ 1 ]->st[ 1 ] - t) - (dv[ 1 ]->st[ 0 ] - s) * (dv[ 0 ]->st[ 1 ] - t)) / bb;\r
314                 \r
315                 ttv[ i ][ 0 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 0 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 0 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 0 ];\r
316                 ttv[ i ][ 1 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 1 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 1 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 1 ];\r
317                 ttv[ i ][ 2 ] = bary[ 0 ] * dv[ 0 ]->xyz[ 2 ] + bary[ 1 ] * dv[ 1 ]->xyz[ 2 ] + bary[ 2 ] * dv[ 2 ]->xyz[ 2 ];\r
318                 \r
319                 VectorSubtract( ttv[ i ], dv[ i ]->xyz, ttv[ i ] );\r
320                 VectorNormalize( ttv[ i ], ttv[ i ] );\r
321                 \r
322                 /* debug code */\r
323                 //%     Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i,\r
324                 //%             stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] );\r
325         }\r
326         \r
327         /* return to caller */\r
328         return qtrue;\r
329 }\r
330 \r
331 \r
332 \r
333 \r
334 /*\r
335 PerturbNormal()\r
336 perterbs the normal by the shader's normalmap in tangent space\r
337 */\r
338 \r
339 static void PerturbNormal( bspDrawVert_t *dv, shaderInfo_t *si, vec3_t pNormal, vec3_t stv[ 3 ], vec3_t ttv[ 3 ] )\r
340 {\r
341         int                     i;\r
342         vec4_t          bump;\r
343         \r
344         \r
345         /* passthrough */\r
346         VectorCopy( dv->normal, pNormal );\r
347         \r
348         /* sample normalmap */\r
349         if( RadSampleImage( si->normalImage->pixels, si->normalImage->width, si->normalImage->height, dv->st, bump ) == qfalse )\r
350                 return;\r
351         \r
352         /* remap sampled normal from [0,255] to [-1,-1] */\r
353         for( i = 0; i < 3; i++ )\r
354                 bump[ i ] = (bump[ i ] - 127.0f) * (1.0f / 127.5f);\r
355         \r
356         /* scale tangent vectors and add to original normal */\r
357         VectorMA( dv->normal, bump[ 0 ], stv[ 0 ], pNormal );\r
358         VectorMA( pNormal, bump[ 1 ], ttv[ 0 ], pNormal );\r
359         VectorMA( pNormal, bump[ 2 ], dv->normal, pNormal );\r
360         \r
361         /* renormalize and return */\r
362         VectorNormalize( pNormal, pNormal );\r
363 }\r
364 \r
365 \r
366 \r
367 /*\r
368 MapSingleLuxel()\r
369 maps a luxel for triangle bv at\r
370 */\r
371 \r
372 #define NUDGE                   0.5f\r
373 #define BOGUS_NUDGE             -99999.0f\r
374 \r
375 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 ] )\r
376 {\r
377         int                             i, x, y, numClusters, *clusters, pointCluster, *cluster;\r
378         float                   *luxel, *origin, *normal, d, lightmapSampleOffset;\r
379         shaderInfo_t    *si;\r
380         vec3_t                  pNormal;\r
381         vec3_t                  vecs[ 3 ];\r
382         vec3_t                  nudged;\r
383         float                   *nudge;\r
384         static float    nudges[][ 2 ] =\r
385                                         {\r
386                                                 //%{ 0, 0 },            /* try center first */\r
387                                                 { -NUDGE, 0 },          /* left */\r
388                                                 { NUDGE, 0 },           /* right */\r
389                                                 { 0, NUDGE },           /* up */\r
390                                                 { 0, -NUDGE },          /* down */\r
391                                                 { -NUDGE, NUDGE },      /* left/up */\r
392                                                 { NUDGE, -NUDGE },      /* right/down */\r
393                                                 { NUDGE, NUDGE },       /* right/up */\r
394                                                 { -NUDGE, -NUDGE },     /* left/down */\r
395                                                 { BOGUS_NUDGE, BOGUS_NUDGE }\r
396                                         };\r
397         \r
398         \r
399         /* find luxel xy coords (fixme: subtract 0.5?) */\r
400         x = dv->lightmap[ 0 ][ 0 ];\r
401         y = dv->lightmap[ 0 ][ 1 ];\r
402         if( x < 0 )\r
403                 x = 0;\r
404         else if( x >= lm->sw )\r
405                 x = lm->sw - 1;\r
406         if( y < 0 )\r
407                 y = 0;\r
408         else if( y >= lm->sh )\r
409                 y = lm->sh - 1;\r
410         \r
411         /* set shader and cluster list */\r
412         if( info != NULL )\r
413         {\r
414                 si = info->si;\r
415                 numClusters = info->numSurfaceClusters;\r
416                 clusters = &surfaceClusters[ info->firstSurfaceCluster ];\r
417         }\r
418         else\r
419         {\r
420                 si = NULL;\r
421                 numClusters = 0;\r
422                 clusters = NULL;\r
423         }\r
424         \r
425         /* get luxel, origin, cluster, and normal */\r
426         luxel = SUPER_LUXEL( 0, x, y );\r
427         origin = SUPER_ORIGIN( x, y );\r
428         normal = SUPER_NORMAL( x, y );\r
429         cluster = SUPER_CLUSTER( x, y );\r
430         \r
431         /* don't attempt to remap occluded luxels for planar surfaces */\r
432         if( (*cluster) == CLUSTER_OCCLUDED && lm->plane != NULL )\r
433                 return (*cluster);\r
434         \r
435         /* only average the normal for premapped luxels */\r
436         else if( (*cluster) >= 0 )\r
437         {\r
438                 /* do bumpmap calculations */\r
439                 if( stv != NULL )\r
440                         PerturbNormal( dv, si, pNormal, stv, ttv );\r
441                 else\r
442                         VectorCopy( dv->normal, pNormal );\r
443                 \r
444                 /* add the additional normal data */\r
445                 VectorAdd( normal, pNormal, normal );\r
446                 luxel[ 3 ] += 1.0f;\r
447                 return (*cluster);\r
448         }\r
449         \r
450         /* otherwise, unmapped luxels (*cluster == CLUSTER_UNMAPPED) will have their full attributes calculated */\r
451         \r
452         /* get origin */\r
453         \r
454         /* axial lightmap projection */\r
455         if( lm->vecs != NULL )\r
456         {\r
457                 /* calculate an origin for the sample from the lightmap vectors */\r
458                 VectorCopy( lm->origin, origin );\r
459                 for( i = 0; i < 3; i++ )\r
460                 {\r
461                         /* add unless it's the axis, which is taken care of later */\r
462                         if( i == lm->axisNum )\r
463                                 continue;\r
464                         origin[ i ] += (x * lm->vecs[ 0 ][ i ]) + (y * lm->vecs[ 1 ][ i ]);\r
465                 }\r
466                 \r
467                 /* project the origin onto the plane */\r
468                 d = DotProduct( origin, plane ) - plane[ 3 ];\r
469                 d /= plane[ lm->axisNum ];\r
470                 origin[ lm->axisNum ] -= d;\r
471         }\r
472         \r
473         /* non axial lightmap projection (explicit xyz) */\r
474         else\r
475                 VectorCopy( dv->xyz, origin );\r
476         \r
477         /* planar surfaces have precalculated lightmap vectors for nudging */\r
478         if( lm->plane != NULL )\r
479         {\r
480                 VectorCopy( lm->vecs[ 0 ], vecs[ 0 ] );\r
481                 VectorCopy( lm->vecs[ 1 ], vecs[ 1 ] );\r
482                 VectorCopy( lm->plane, vecs[ 2 ] );\r
483         }\r
484         \r
485         /* non-planar surfaces must calculate them */\r
486         else\r
487         {\r
488                 if( plane != NULL )\r
489                         VectorCopy( plane, vecs[ 2 ] );\r
490                 else\r
491                         VectorCopy( dv->normal, vecs[ 2 ] );\r
492                 MakeNormalVectors( vecs[ 2 ], vecs[ 0 ], vecs[ 1 ] );\r
493         }\r
494         \r
495         /* push the origin off the surface a bit */\r
496         if( si != NULL )\r
497                 lightmapSampleOffset = si->lightmapSampleOffset;\r
498         else\r
499                 lightmapSampleOffset = DEFAULT_LIGHTMAP_SAMPLE_OFFSET;\r
500         if( lm->axisNum < 0 )\r
501                 VectorMA( origin, lightmapSampleOffset, vecs[ 2 ], origin );\r
502         else if( vecs[ 2 ][ lm->axisNum ] < 0.0f )\r
503                 origin[ lm->axisNum ] -= lightmapSampleOffset;\r
504         else\r
505                 origin[ lm->axisNum ] += lightmapSampleOffset;\r
506         \r
507         /* get cluster */\r
508         pointCluster = ClusterForPointExtFilter( origin, LUXEL_EPSILON, numClusters, clusters );\r
509         \r
510         /* another retarded hack, storing nudge count in luxel[ 1 ] */\r
511         luxel[ 1 ] = 0.0f;      \r
512         \r
513         /* point in solid? */\r
514         if( pointCluster < 0 )\r
515         {\r
516                 /* nudge the the location around */\r
517                 nudge = nudges[ 0 ];\r
518                 while( nudge[ 0 ] > BOGUS_NUDGE && pointCluster < 0 )\r
519                 {\r
520                         /* nudge the vector around a bit */\r
521                         for( i = 0; i < 3; i++ )\r
522                         {\r
523                                 /* set nudged point*/\r
524                                 nudged[ i ] = origin[ i ] + (nudge[ 0 ] * vecs[ 0 ][ i ]) + (nudge[ 1 ] * vecs[ 1 ][ i ]);\r
525                         }\r
526                         nudge += 2;\r
527                         \r
528                         /* get pvs cluster */\r
529                         pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters ); //% + 0.625 );\r
530                         if( pointCluster >= 0 ) \r
531                                 VectorCopy( nudged, origin );\r
532                         luxel[ 1 ] += 1.0f;\r
533                 }\r
534         }\r
535         \r
536         /* as a last resort, if still in solid, try drawvert origin offset by normal */\r
537         if( pointCluster < 0 && si != NULL )\r
538         {\r
539                 VectorMA( dv->xyz, lightmapSampleOffset, dv->normal, nudged );\r
540                 pointCluster = ClusterForPointExtFilter( nudged, LUXEL_EPSILON, numClusters, clusters );\r
541                 if( pointCluster >= 0 )\r
542                         VectorCopy( nudged, origin );\r
543                 luxel[ 1 ] += 1.0f;\r
544         }\r
545         \r
546         /* valid? */\r
547         if( pointCluster < 0 )\r
548         {\r
549                 (*cluster) = CLUSTER_OCCLUDED;\r
550                 VectorClear( origin );\r
551                 VectorClear( normal );\r
552                 numLuxelsOccluded++;\r
553                 return (*cluster);\r
554         }\r
555         \r
556         /* debug code */\r
557         //%     Sys_Printf( "%f %f %f\n", origin[ 0 ], origin[ 1 ], origin[ 2 ] );\r
558         \r
559         /* do bumpmap calculations */\r
560         if( stv )\r
561                 PerturbNormal( dv, si, pNormal, stv, ttv );\r
562         else\r
563                 VectorCopy( dv->normal, pNormal );\r
564         \r
565         /* store the cluster and normal */\r
566         (*cluster) = pointCluster;\r
567         VectorCopy( pNormal, normal );\r
568         \r
569         /* store explicit mapping pass and implicit mapping pass */\r
570         luxel[ 0 ] = pass;\r
571         luxel[ 3 ] = 1.0f;\r
572         \r
573         /* add to count */\r
574         numLuxelsMapped++;\r
575         \r
576         /* return ok */\r
577         return (*cluster);\r
578 }\r
579 \r
580 \r
581 \r
582 /*\r
583 MapTriangle_r()\r
584 recursively subdivides a triangle until its edges are shorter\r
585 than the distance between two luxels (thanks jc :)\r
586 */\r
587 \r
588 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 ] )\r
589 {\r
590         bspDrawVert_t   mid, *dv2[ 3 ];\r
591         int                             max;\r
592         \r
593         \r
594         /* map the vertexes */\r
595         #if 0\r
596         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );\r
597         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );\r
598         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );\r
599         #endif\r
600         \r
601         /* subdivide calc */\r
602         {\r
603                 int                     i;\r
604                 float           *a, *b, dx, dy, dist, maxDist;\r
605                 \r
606                 \r
607                 /* find the longest edge and split it */\r
608                 max = -1;\r
609                 maxDist = 0;\r
610                 for( i = 0; i < 3; i++ )\r
611                 {\r
612                         /* get verts */\r
613                         a = dv[ i ]->lightmap[ 0 ];\r
614                         b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];\r
615                         \r
616                         /* get dists */\r
617                         dx = a[ 0 ] - b[ 0 ];\r
618                         dy = a[ 1 ] - b[ 1 ];\r
619                         dist = (dx * dx) + (dy * dy);   //% sqrt( (dx * dx) + (dy * dy) );\r
620                         \r
621                         /* longer? */\r
622                         if( dist > maxDist )\r
623                         {\r
624                                 maxDist = dist;\r
625                                 max = i;\r
626                         }\r
627                 }\r
628                 \r
629                 /* try to early out */\r
630                 if( max < 0 || maxDist <= subdivideThreshold )  /* ydnar: was i < 0 instead of max < 0 (?) */\r
631                         return;\r
632         }\r
633         \r
634         /* split the longest edge and map it */\r
635         LerpDrawVert( dv[ max ], dv[ (max + 1) % 3 ], &mid );\r
636         MapSingleLuxel( lm, info, &mid, plane, 1, stv, ttv );\r
637         \r
638         /* push the point up a little bit to account for fp creep (fixme: revisit this) */\r
639         //%     VectorMA( mid.xyz, 2.0f, mid.normal, mid.xyz );\r
640         \r
641         /* recurse to first triangle */\r
642         VectorCopy( dv, dv2 );\r
643         dv2[ max ] = &mid;\r
644         MapTriangle_r( lm, info, dv2, plane, stv, ttv );\r
645         \r
646         /* recurse to second triangle */\r
647         VectorCopy( dv, dv2 );\r
648         dv2[ (max + 1) % 3 ] = &mid;\r
649         MapTriangle_r( lm, info, dv2, plane, stv, ttv );\r
650 }\r
651 \r
652 \r
653 \r
654 /*\r
655 MapTriangle()\r
656 seed function for MapTriangle_r()\r
657 requires a cw ordered triangle\r
658 */\r
659 \r
660 static qboolean MapTriangle( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 3 ], qboolean mapNonAxial )\r
661 {\r
662         int                             i;\r
663         vec4_t                  plane;\r
664         vec3_t                  *stv, *ttv, stvStatic[ 3 ], ttvStatic[ 3 ];\r
665         \r
666         \r
667         /* get plane if possible */\r
668         if( lm->plane != NULL )\r
669         {\r
670                 VectorCopy( lm->plane, plane );\r
671                 plane[ 3 ] = lm->plane[ 3 ];\r
672         }\r
673         \r
674         /* otherwise make one from the points */\r
675         else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )\r
676                 return qfalse;\r
677         \r
678         /* check to see if we need to calculate texture->world tangent vectors */\r
679         if( info->si->normalImage != NULL && CalcTangentVectors( 3, dv, stvStatic, ttvStatic ) )\r
680         {\r
681                 stv = stvStatic;\r
682                 ttv = ttvStatic;\r
683         }\r
684         else\r
685         {\r
686                 stv = NULL;\r
687                 ttv = NULL;\r
688         }\r
689         \r
690         /* map the vertexes */\r
691         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );\r
692         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );\r
693         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );\r
694         \r
695         /* 2002-11-20: prefer axial triangle edges */\r
696         if( mapNonAxial )\r
697         {\r
698                 /* subdivide the triangle */\r
699                 MapTriangle_r( lm, info, dv, plane, stv, ttv );\r
700                 return qtrue;\r
701         }\r
702         \r
703         for( i = 0; i < 3; i++ )\r
704         {\r
705                 float                   *a, *b;\r
706                 bspDrawVert_t   *dv2[ 3 ];\r
707                 \r
708                 \r
709                 /* get verts */\r
710                 a = dv[ i ]->lightmap[ 0 ];\r
711                 b = dv[ (i + 1) % 3 ]->lightmap[ 0 ];\r
712                 \r
713                 /* make degenerate triangles for mapping edges */\r
714                 if( fabs( a[ 0 ] - b[ 0 ] ) < 0.01f || fabs( a[ 1 ] - b[ 1 ] ) < 0.01f )\r
715                 {\r
716                         dv2[ 0 ] = dv[ i ];\r
717                         dv2[ 1 ] = dv[ (i + 1) % 3 ];\r
718                         dv2[ 2 ] = dv[ (i + 1) % 3 ];\r
719                         \r
720                         /* map the degenerate triangle */\r
721                         MapTriangle_r( lm, info, dv2, plane, stv, ttv );\r
722                 }\r
723         }\r
724         \r
725         return qtrue;\r
726 }\r
727 \r
728 \r
729 \r
730 /*\r
731 MapQuad_r()\r
732 recursively subdivides a quad until its edges are shorter\r
733 than the distance between two luxels\r
734 */\r
735 \r
736 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 ] )\r
737 {\r
738         bspDrawVert_t   mid[ 2 ], *dv2[ 4 ];\r
739         int                             max;\r
740         \r
741         \r
742         /* subdivide calc */\r
743         {\r
744                 int                     i;\r
745                 float           *a, *b, dx, dy, dist, maxDist;\r
746                 \r
747                 \r
748                 /* find the longest edge and split it */\r
749                 max = -1;\r
750                 maxDist = 0;\r
751                 for( i = 0; i < 4; i++ )\r
752                 {\r
753                         /* get verts */\r
754                         a = dv[ i ]->lightmap[ 0 ];\r
755                         b = dv[ (i + 1) % 4 ]->lightmap[ 0 ];\r
756                         \r
757                         /* get dists */\r
758                         dx = a[ 0 ] - b[ 0 ];\r
759                         dy = a[ 1 ] - b[ 1 ];\r
760                         dist = (dx * dx) + (dy * dy);   //% sqrt( (dx * dx) + (dy * dy) );\r
761                         \r
762                         /* longer? */\r
763                         if( dist > maxDist )\r
764                         {\r
765                                 maxDist = dist;\r
766                                 max = i;\r
767                         }\r
768                 }\r
769                 \r
770                 /* try to early out */\r
771                 if( max < 0 || maxDist <= subdivideThreshold )\r
772                         return;\r
773         }\r
774         \r
775         /* we only care about even/odd edges */\r
776         max &= 1;\r
777         \r
778         /* split the longest edges */\r
779         LerpDrawVert( dv[ max ], dv[ (max + 1) % 4 ], &mid[ 0 ] );\r
780         LerpDrawVert( dv[ max + 2 ], dv[ (max + 3) % 4 ], &mid[ 1 ] );\r
781         \r
782         /* map the vertexes */\r
783         MapSingleLuxel( lm, info, &mid[ 0 ], plane, 1, stv, ttv );\r
784         MapSingleLuxel( lm, info, &mid[ 1 ], plane, 1, stv, ttv );\r
785         \r
786         /* 0 and 2 */\r
787         if( max == 0 )\r
788         {\r
789                 /* recurse to first quad */\r
790                 dv2[ 0 ] = dv[ 0 ];\r
791                 dv2[ 1 ] = &mid[ 0 ];\r
792                 dv2[ 2 ] = &mid[ 1 ];\r
793                 dv2[ 3 ] = dv[ 3 ];\r
794                 MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
795                 \r
796                 /* recurse to second quad */\r
797                 dv2[ 0 ] = &mid[ 0 ];\r
798                 dv2[ 1 ] = dv[ 1 ];\r
799                 dv2[ 2 ] = dv[ 2 ];\r
800                 dv2[ 3 ] = &mid[ 1 ];\r
801                 MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
802         }\r
803         \r
804         /* 1 and 3 */\r
805         else\r
806         {\r
807                 /* recurse to first quad */\r
808                 dv2[ 0 ] = dv[ 0 ];\r
809                 dv2[ 1 ] = dv[ 1 ];\r
810                 dv2[ 2 ] = &mid[ 0 ];\r
811                 dv2[ 3 ] = &mid[ 1 ];\r
812                 MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
813                 \r
814                 /* recurse to second quad */\r
815                 dv2[ 0 ] = &mid[ 1 ];\r
816                 dv2[ 1 ] = &mid[ 0 ];\r
817                 dv2[ 2 ] = dv[ 2 ];\r
818                 dv2[ 3 ] = dv[ 3 ];\r
819                 MapQuad_r( lm, info, dv2, plane, stv, ttv );\r
820         }\r
821 }\r
822 \r
823 \r
824 \r
825 /*\r
826 MapQuad()\r
827 seed function for MapQuad_r()\r
828 requires a cw ordered triangle quad\r
829 */\r
830 \r
831 #define QUAD_PLANAR_EPSILON             0.5f\r
832 \r
833 static qboolean MapQuad( rawLightmap_t *lm, surfaceInfo_t *info, bspDrawVert_t *dv[ 4 ] )\r
834 {\r
835         float                   dist;\r
836         vec4_t                  plane;\r
837         vec3_t                  *stv, *ttv, stvStatic[ 4 ], ttvStatic[ 4 ];\r
838         \r
839         \r
840         /* get plane if possible */\r
841         if( lm->plane != NULL )\r
842         {\r
843                 VectorCopy( lm->plane, plane );\r
844                 plane[ 3 ] = lm->plane[ 3 ];\r
845         }\r
846         \r
847         /* otherwise make one from the points */\r
848         else if( PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz ) == qfalse )\r
849                 return qfalse;\r
850         \r
851         /* 4th point must fall on the plane */\r
852         dist = DotProduct( plane, dv[ 3 ]->xyz ) - plane[ 3 ];\r
853         if( fabs( dist ) > QUAD_PLANAR_EPSILON )\r
854                 return qfalse;\r
855         \r
856         /* check to see if we need to calculate texture->world tangent vectors */\r
857         if( info->si->normalImage != NULL && CalcTangentVectors( 4, dv, stvStatic, ttvStatic ) )\r
858         {\r
859                 stv = stvStatic;\r
860                 ttv = ttvStatic;\r
861         }\r
862         else\r
863         {\r
864                 stv = NULL;\r
865                 ttv = NULL;\r
866         }\r
867         \r
868         /* map the vertexes */\r
869         MapSingleLuxel( lm, info, dv[ 0 ], plane, 1, stv, ttv );\r
870         MapSingleLuxel( lm, info, dv[ 1 ], plane, 1, stv, ttv );\r
871         MapSingleLuxel( lm, info, dv[ 2 ], plane, 1, stv, ttv );\r
872         MapSingleLuxel( lm, info, dv[ 3 ], plane, 1, stv, ttv );\r
873         \r
874         /* subdivide the quad */\r
875         MapQuad_r( lm, info, dv, plane, stv, ttv );\r
876         return qtrue;\r
877 }\r
878 \r
879 \r
880 \r
881 /*\r
882 MapRawLightmap()\r
883 maps the locations, normals, and pvs clusters for a raw lightmap\r
884 */\r
885 \r
886 #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)\r
887 \r
888 void MapRawLightmap( int rawLightmapNum )\r
889 {\r
890         int                                     n, num, i, x, y, sx, sy, pw[ 5 ], r, *cluster, mapNonAxial;\r
891         float                           *luxel, *origin, *normal, samples, radius, pass;\r
892         rawLightmap_t           *lm;\r
893         bspDrawSurface_t        *ds;\r
894         surfaceInfo_t           *info;\r
895         mesh_t                          src, *subdivided, *mesh;\r
896         bspDrawVert_t           *verts, *dv[ 4 ], fake;\r
897         \r
898         \r
899         /* bail if this number exceeds the number of raw lightmaps */\r
900         if( rawLightmapNum >= numRawLightmaps )\r
901                 return;\r
902         \r
903         /* get lightmap */\r
904         lm = &rawLightmaps[ rawLightmapNum ];\r
905         \r
906         /* -----------------------------------------------------------------\r
907            map referenced surfaces onto the raw lightmap\r
908            ----------------------------------------------------------------- */\r
909         \r
910         /* walk the list of surfaces on this raw lightmap */\r
911         for( n = 0; n < lm->numLightSurfaces; n++ )\r
912         {\r
913                 /* with > 1 surface per raw lightmap, clear occluded */\r
914                 if( n > 0 )\r
915                 {\r
916                         for( y = 0; y < lm->sh; y++ )\r
917                         {\r
918                                 for( x = 0; x < lm->sw; x++ )\r
919                                 {\r
920                                         /* get cluster */\r
921                                         cluster = SUPER_CLUSTER( x, y );\r
922                                         if( *cluster < 0 )\r
923                                                 *cluster = CLUSTER_UNMAPPED;\r
924                                 }\r
925                         }\r
926                 }\r
927                 \r
928                 /* get surface */\r
929                 num = lightSurfaces[ lm->firstLightSurface + n ];\r
930                 ds = &bspDrawSurfaces[ num ];\r
931                 info = &surfaceInfos[ num ];\r
932                 \r
933                 /* bail if no lightmap to calculate */\r
934                 if( info->lm != lm )\r
935                 {\r
936                         Sys_Printf( "!" );\r
937                         continue;\r
938                 }\r
939                 \r
940                 /* map the surface onto the lightmap origin/cluster/normal buffers */\r
941                 switch( ds->surfaceType )\r
942                 {\r
943                         case MST_PLANAR:\r
944                                 /* get verts */\r
945                                 verts = yDrawVerts + ds->firstVert;\r
946                                 \r
947                                 /* map the triangles */\r
948                                 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )\r
949                                 {\r
950                                         for( i = 0; i < ds->numIndexes; i += 3 )\r
951                                         {\r
952                                                 dv[ 0 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i ] ];\r
953                                                 dv[ 1 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 1 ] ];\r
954                                                 dv[ 2 ] = &verts[ bspDrawIndexes[ ds->firstIndex + i + 2 ] ];\r
955                                                 MapTriangle( lm, info, dv, mapNonAxial );\r
956                                         }\r
957                                 }\r
958                                 break;\r
959                         \r
960                         case MST_PATCH:\r
961                                 /* make a mesh from the drawsurf */ \r
962                                 src.width = ds->patchWidth;\r
963                                 src.height = ds->patchHeight;\r
964                                 src.verts = &yDrawVerts[ ds->firstVert ];\r
965                                 //%     subdivided = SubdivideMesh( src, 8, 512 );\r
966                                 subdivided = SubdivideMesh2( src, info->patchIterations );\r
967                                 \r
968                                 /* fit it to the curve and remove colinear verts on rows/columns */\r
969                                 PutMeshOnCurve( *subdivided );\r
970                                 mesh = RemoveLinearMeshColumnsRows( subdivided );\r
971                                 FreeMesh( subdivided );\r
972                                 \r
973                                 /* get verts */\r
974                                 verts = mesh->verts;\r
975                                 \r
976                                 /* debug code */\r
977                                 #if 0\r
978                                         if( lm->plane )\r
979                                         {\r
980                                                 Sys_Printf( "Planar patch: [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f] [%1.3f %1.3f %1.3f]\n",\r
981                                                         lm->plane[ 0 ], lm->plane[ 1 ], lm->plane[ 2 ],\r
982                                                         lm->vecs[ 0 ][ 0 ], lm->vecs[ 0 ][ 1 ], lm->vecs[ 0 ][ 2 ],\r
983                                                         lm->vecs[ 1 ][ 0 ], lm->vecs[ 1 ][ 1 ], lm->vecs[ 1 ][ 2 ] );\r
984                                         }\r
985                                 #endif\r
986                                 \r
987                                 /* map the mesh quads */\r
988                                 #if 0\r
989 \r
990                                 for( mapNonAxial = 0; mapNonAxial < 2; mapNonAxial++ )\r
991                                 {\r
992                                         for( y = 0; y < (mesh->height - 1); y++ )\r
993                                         {\r
994                                                 for( x = 0; x < (mesh->width - 1); x++ )\r
995                                                 {\r
996                                                         /* set indexes */\r
997                                                         pw[ 0 ] = x + (y * mesh->width);\r
998                                                         pw[ 1 ] = x + ((y + 1) * mesh->width);\r
999                                                         pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
1000                                                         pw[ 3 ] = x + 1 + (y * mesh->width);\r
1001                                                         pw[ 4 ] = x + (y * mesh->width);        /* same as pw[ 0 ] */\r
1002                                                         \r
1003                                                         /* set radix */\r
1004                                                         r = (x + y) & 1;\r
1005                                                         \r
1006                                                         /* get drawverts and map first triangle */\r
1007                                                         dv[ 0 ] = &verts[ pw[ r + 0 ] ];\r
1008                                                         dv[ 1 ] = &verts[ pw[ r + 1 ] ];\r
1009                                                         dv[ 2 ] = &verts[ pw[ r + 2 ] ];\r
1010                                                         MapTriangle( lm, info, dv, mapNonAxial );\r
1011                                                         \r
1012                                                         /* get drawverts and map second triangle */\r
1013                                                         dv[ 0 ] = &verts[ pw[ r + 0 ] ];\r
1014                                                         dv[ 1 ] = &verts[ pw[ r + 2 ] ];\r
1015                                                         dv[ 2 ] = &verts[ pw[ r + 3 ] ];\r
1016                                                         MapTriangle( lm, info, dv, mapNonAxial );\r
1017                                                 }\r
1018                                         }\r
1019                                 }\r
1020                                 \r
1021                                 #else\r
1022                                 \r
1023                                 for( y = 0; y < (mesh->height - 1); y++ )\r
1024                                 {\r
1025                                         for( x = 0; x < (mesh->width - 1); x++ )\r
1026                                         {\r
1027                                                 /* set indexes */\r
1028                                                 pw[ 0 ] = x + (y * mesh->width);\r
1029                                                 pw[ 1 ] = x + ((y + 1) * mesh->width);\r
1030                                                 pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
1031                                                 pw[ 3 ] = x + 1 + (y * mesh->width);\r
1032                                                 pw[ 4 ] = pw[ 0 ];\r
1033                                                 \r
1034                                                 /* set radix */\r
1035                                                 r = (x + y) & 1;\r
1036                                                 \r
1037                                                 /* attempt to map quad first */\r
1038                                                 dv[ 0 ] = &verts[ pw[ r + 0 ] ];\r
1039                                                 dv[ 1 ] = &verts[ pw[ r + 1 ] ];\r
1040                                                 dv[ 2 ] = &verts[ pw[ r + 2 ] ];\r
1041                                                 dv[ 3 ] = &verts[ pw[ r + 3 ] ];\r
1042                                                 if( MapQuad( lm, info, dv ) )\r
1043                                                         continue;\r
1044                                                 \r
1045                                                 /* get drawverts and map first triangle */\r
1046                                                 MapTriangle( lm, info, dv, mapNonAxial );\r
1047                                                 \r
1048                                                 /* get drawverts and map second triangle */\r
1049                                                 dv[ 1 ] = &verts[ pw[ r + 2 ] ];\r
1050                                                 dv[ 2 ] = &verts[ pw[ r + 3 ] ];\r
1051                                                 MapTriangle( lm, info, dv, mapNonAxial );\r
1052                                         }\r
1053                                 }\r
1054                                 \r
1055                                 #endif\r
1056                                 \r
1057                                 /* free the mesh */\r
1058                                 FreeMesh( mesh );\r
1059                                 break;\r
1060                         \r
1061                         default:\r
1062                                 break;\r
1063                 }\r
1064         }\r
1065         \r
1066         /* -----------------------------------------------------------------\r
1067            average and clean up luxel normals\r
1068            ----------------------------------------------------------------- */\r
1069         \r
1070         /* walk the luxels */\r
1071         for( y = 0; y < lm->sh; y++ )\r
1072         {\r
1073                 for( x = 0; x < lm->sw; x++ )\r
1074                 {\r
1075                         /* get luxel */\r
1076                         luxel = SUPER_LUXEL( 0, x, y );\r
1077                         normal = SUPER_NORMAL( x, y );\r
1078                         cluster = SUPER_CLUSTER( x, y );\r
1079 \r
1080                         /* only look at mapped luxels */\r
1081                         if( *cluster < 0 )\r
1082                                 continue;\r
1083                         \r
1084                         /* the normal data could be the sum of multiple samples */\r
1085                         if( luxel[ 3 ] > 1.0f )\r
1086                                 VectorNormalize( normal, normal );\r
1087                         \r
1088                         /* mark this luxel as having only one normal */\r
1089                         luxel[ 3 ] = 1.0f;\r
1090                 }\r
1091         }\r
1092         \r
1093         /* non-planar surfaces stop here */\r
1094         if( lm->plane == NULL )\r
1095                 return;\r
1096         \r
1097         /* -----------------------------------------------------------------\r
1098            map occluded or unuxed luxels\r
1099            ----------------------------------------------------------------- */\r
1100         \r
1101         /* walk the luxels */\r
1102         radius = floor( superSample / 2 );\r
1103         radius = radius > 0 ? radius : 1.0f;\r
1104         radius += 1.0f;\r
1105         for( pass = 2.0f; pass <= radius; pass += 1.0f )\r
1106         {\r
1107                 for( y = 0; y < lm->sh; y++ )\r
1108                 {\r
1109                         for( x = 0; x < lm->sw; x++ )\r
1110                         {\r
1111                                 /* get luxel */\r
1112                                 luxel = SUPER_LUXEL( 0, x, y );\r
1113                                 normal = SUPER_NORMAL( x, y );\r
1114                                 cluster = SUPER_CLUSTER( x, y );\r
1115                                 \r
1116                                 /* only look at unmapped luxels */\r
1117                                 if( *cluster != CLUSTER_UNMAPPED )\r
1118                                         continue;\r
1119                                 \r
1120                                 /* divine a normal and origin from neighboring luxels */\r
1121                                 VectorClear( fake.xyz );\r
1122                                 VectorClear( fake.normal );\r
1123                                 fake.lightmap[ 0 ][ 0 ] = x;    //% 0.0001 + x;\r
1124                                 fake.lightmap[ 0 ][ 1 ] = y;    //% 0.0001 + y;\r
1125                                 samples = 0.0f;\r
1126                                 for( sy = (y - 1); sy <= (y + 1); sy++ )\r
1127                                 {\r
1128                                         if( sy < 0 || sy >= lm->sh )\r
1129                                                 continue;\r
1130                                         \r
1131                                         for( sx = (x - 1); sx <= (x + 1); sx++ )\r
1132                                         {\r
1133                                                 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )\r
1134                                                         continue;\r
1135                                                 \r
1136                                                 /* get neighboring luxel */\r
1137                                                 luxel = SUPER_LUXEL( 0, sx, sy );\r
1138                                                 origin = SUPER_ORIGIN( sx, sy );\r
1139                                                 normal = SUPER_NORMAL( sx, sy );\r
1140                                                 cluster = SUPER_CLUSTER( sx, sy );\r
1141                                                 \r
1142                                                 /* only consider luxels mapped in previous passes */\r
1143                                                 if( *cluster < 0 || luxel[ 0 ] >= pass )\r
1144                                                         continue;\r
1145                                                 \r
1146                                                 /* add its distinctiveness to our own */\r
1147                                                 VectorAdd( fake.xyz, origin, fake.xyz );\r
1148                                                 VectorAdd( fake.normal, normal, fake.normal );\r
1149                                                 samples += luxel[ 3 ];\r
1150                                         }\r
1151                                 }\r
1152                                 \r
1153                                 /* any samples? */\r
1154                                 if( samples == 0.0f )\r
1155                                         continue;\r
1156                                 \r
1157                                 /* average */\r
1158                                 VectorDivide( fake.xyz, samples, fake.xyz );\r
1159                                 //%     VectorDivide( fake.normal, samples, fake.normal );\r
1160                                 if( VectorNormalize( fake.normal, fake.normal ) == 0.0f )\r
1161                                         continue;\r
1162                                 \r
1163                                 /* map the fake vert */\r
1164                                 MapSingleLuxel( lm, NULL, &fake, lm->plane, pass, NULL, NULL );\r
1165                         }\r
1166                 }\r
1167         }\r
1168         \r
1169         /* -----------------------------------------------------------------\r
1170            average and clean up luxel normals\r
1171            ----------------------------------------------------------------- */\r
1172         \r
1173         /* walk the luxels */\r
1174         for( y = 0; y < lm->sh; y++ )\r
1175         {\r
1176                 for( x = 0; x < lm->sw; x++ )\r
1177                 {\r
1178                         /* get luxel */\r
1179                         luxel = SUPER_LUXEL( 0, x, y );\r
1180                         normal = SUPER_NORMAL( x, y );\r
1181                         cluster = SUPER_CLUSTER( x, y );\r
1182                         \r
1183                         /* only look at mapped luxels */\r
1184                         if( *cluster < 0 )\r
1185                                 continue;\r
1186                         \r
1187                         /* the normal data could be the sum of multiple samples */\r
1188                         if( luxel[ 3 ] > 1.0f )\r
1189                                 VectorNormalize( normal, normal );\r
1190                         \r
1191                         /* mark this luxel as having only one normal */\r
1192                         luxel[ 3 ] = 1.0f;\r
1193                 }\r
1194         }\r
1195         \r
1196         /* debug code */\r
1197         #if 0\r
1198                 Sys_Printf( "\n" );\r
1199                 for( y = 0; y < lm->sh; y++ )\r
1200                 {\r
1201                         for( x = 0; x < lm->sw; x++ )\r
1202                         {\r
1203                                 vec3_t  mins, maxs;\r
1204                                 \r
1205 \r
1206                                 cluster = SUPER_CLUSTER( x, y );\r
1207                                 origin = SUPER_ORIGIN( x, y );\r
1208                                 normal = SUPER_NORMAL( x, y );\r
1209                                 luxel = SUPER_LUXEL( x, y );\r
1210                                 \r
1211                                 if( *cluster < 0 )\r
1212                                         continue;\r
1213                                 \r
1214                                 /* check if within the bounding boxes of all surfaces referenced */\r
1215                                 ClearBounds( mins, maxs );\r
1216                                 for( n = 0; n < lm->numLightSurfaces; n++ )\r
1217                                 {\r
1218                                         int TOL;\r
1219                                         info = &surfaceInfos[ lightSurfaces[ lm->firstLightSurface + n ] ];\r
1220                                         TOL = info->sampleSize + 2;\r
1221                                         AddPointToBounds( info->mins, mins, maxs );\r
1222                                         AddPointToBounds( info->maxs, mins, maxs );\r
1223                                         if( origin[ 0 ] > (info->mins[ 0 ] - TOL) && origin[ 0 ] < (info->maxs[ 0 ] + TOL) &&\r
1224                                                 origin[ 1 ] > (info->mins[ 1 ] - TOL) && origin[ 1 ] < (info->maxs[ 1 ] + TOL) &&\r
1225                                                 origin[ 2 ] > (info->mins[ 2 ] - TOL) && origin[ 2 ] < (info->maxs[ 2 ] + TOL) )\r
1226                                                 break;\r
1227                                 }\r
1228                                 \r
1229                                 /* inside? */\r
1230                                 if( n < lm->numLightSurfaces )\r
1231                                         continue;\r
1232                                 \r
1233                                 /* report bogus origin */\r
1234                                 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",\r
1235                                         rawLightmapNum, x, y, *cluster,\r
1236                                         origin[ 0 ], origin[ 1 ], origin[ 2 ],\r
1237                                         mins[ 0 ], mins[ 1 ], mins[ 2 ],\r
1238                                         maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],\r
1239                                         luxel[ 3 ] );\r
1240                         }\r
1241                 }\r
1242         #endif\r
1243 }\r
1244 \r
1245 \r
1246 \r
1247 /*\r
1248 SubmapRawLuxel()\r
1249 calculates the pvs cluster, origin, normal of a sub-luxel\r
1250 */\r
1251 \r
1252 static qboolean SubmapRawLuxel( rawLightmap_t *lm, int x, int y, float bx, float by, int *sampleCluster, vec3_t sampleOrigin, vec3_t sampleNormal )\r
1253 {\r
1254         int                     i, *cluster, *cluster2;\r
1255         float           *origin, *origin2, *normal;     //%     , *normal2;\r
1256         vec3_t          originVecs[ 2 ];                        //%     , normalVecs[ 2 ];\r
1257         \r
1258         \r
1259         /* calulate x vector */\r
1260         if( (x < (lm->sw - 1) && bx >= 0.0f) || (x == 0 && bx <= 0.0f) )\r
1261         {\r
1262                 cluster = SUPER_CLUSTER( x, y );\r
1263                 origin = SUPER_ORIGIN( x, y );\r
1264                 //%     normal = SUPER_NORMAL( x, y );\r
1265                 cluster2 = SUPER_CLUSTER( x + 1, y );\r
1266                 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x + 1, y );\r
1267                 //%     normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x + 1, y );\r
1268         }\r
1269         else if( (x > 0 && bx <= 0.0f) || (x == (lm->sw - 1) && bx >= 0.0f) )\r
1270         {\r
1271                 cluster = SUPER_CLUSTER( x - 1, y );\r
1272                 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x - 1, y );\r
1273                 //%     normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x - 1, y );\r
1274                 cluster2 = SUPER_CLUSTER( x, y );\r
1275                 origin2 = SUPER_ORIGIN( x, y );\r
1276                 //%     normal2 = SUPER_NORMAL( x, y );\r
1277         }\r
1278         else\r
1279                 Sys_Printf( "WARNING: Spurious lightmap S vector\n" );\r
1280         \r
1281         VectorSubtract( origin2, origin, originVecs[ 0 ] );\r
1282         //%     VectorSubtract( normal2, normal, normalVecs[ 0 ] );\r
1283         \r
1284         /* calulate y vector */\r
1285         if( (y < (lm->sh - 1) && bx >= 0.0f) || (y == 0 && bx <= 0.0f) )\r
1286         {\r
1287                 cluster = SUPER_CLUSTER( x, y );\r
1288                 origin = SUPER_ORIGIN( x, y );\r
1289                 //%     normal = SUPER_NORMAL( x, y );\r
1290                 cluster2 = SUPER_CLUSTER( x, y + 1 );\r
1291                 origin2 = *cluster2 < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y + 1 );\r
1292                 //%     normal2 = *cluster2 < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y + 1 );\r
1293         }\r
1294         else if( (y > 0 && bx <= 0.0f) || (y == (lm->sh - 1) && bx >= 0.0f) )\r
1295         {\r
1296                 cluster = SUPER_CLUSTER( x, y - 1 );\r
1297                 origin = *cluster < 0 ? SUPER_ORIGIN( x, y ) : SUPER_ORIGIN( x, y - 1 );\r
1298                 //%     normal = *cluster < 0 ? SUPER_NORMAL( x, y ) : SUPER_NORMAL( x, y - 1 );\r
1299                 cluster2 = SUPER_CLUSTER( x, y );\r
1300                 origin2 = SUPER_ORIGIN( x, y );\r
1301                 //%     normal2 = SUPER_NORMAL( x, y );\r
1302         }\r
1303         else\r
1304                 Sys_Printf( "WARNING: Spurious lightmap T vector\n" );\r
1305         \r
1306         VectorSubtract( origin2, origin, originVecs[ 1 ] );\r
1307         //%     VectorSubtract( normal2, normal, normalVecs[ 1 ] );\r
1308         \r
1309         /* calculate new origin */\r
1310         //%     VectorMA( origin, bx, originVecs[ 0 ], sampleOrigin );\r
1311         //%     VectorMA( sampleOrigin, by, originVecs[ 1 ], sampleOrigin );\r
1312         for( i = 0; i < 3; i++ )\r
1313                 sampleOrigin[ i ] = sampleOrigin[ i ] + (bx * originVecs[ 0 ][ i ]) + (by * originVecs[ 1 ][ i ]);\r
1314         \r
1315         /* get cluster */\r
1316         *sampleCluster = ClusterForPointExtFilter( sampleOrigin, (LUXEL_EPSILON * 2), lm->numLightClusters, lm->lightClusters );\r
1317         if( *sampleCluster < 0 )\r
1318                 return qfalse;\r
1319         \r
1320         /* calculate new normal */\r
1321         //%     VectorMA( normal, bx, normalVecs[ 0 ], sampleNormal );\r
1322         //%     VectorMA( sampleNormal, by, normalVecs[ 1 ], sampleNormal );\r
1323         //%     if( VectorNormalize( sampleNormal, sampleNormal ) <= 0.0f )\r
1324         //%             return qfalse;\r
1325         normal = SUPER_NORMAL( x, y );\r
1326         VectorCopy( normal, sampleNormal );\r
1327         \r
1328         /* return ok */\r
1329         return qtrue;\r
1330 }\r
1331 \r
1332 \r
1333 /*\r
1334 SubsampleRawLuxel_r()\r
1335 recursively subsamples a luxel until its color gradient is low enough or subsampling limit is reached\r
1336 */\r
1337 \r
1338 void SubsampleRawLuxel_r( rawLightmap_t *lm, trace_t *trace, vec3_t sampleOrigin, int x, int y, float bias, float *lightLuxel )\r
1339 {\r
1340         int                     b, samples, mapped, lighted;\r
1341         int                     cluster[ 4 ];\r
1342         vec4_t          luxel[ 4 ];\r
1343         vec3_t          origin[ 4 ], normal[ 4 ];\r
1344         float           biasDirs[ 4 ][ 2 ] = { { -1.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f } };\r
1345         vec3_t          color, total;\r
1346         \r
1347         \r
1348         /* limit check */\r
1349         if( lightLuxel[ 3 ] >= lightSamples )\r
1350                 return;\r
1351         \r
1352         /* setup */\r
1353         VectorClear( total );\r
1354         mapped = 0;\r
1355         lighted = 0;\r
1356         \r
1357         /* make 2x2 subsample stamp */\r
1358         for( b = 0; b < 4; b++ )\r
1359         {\r
1360                 /* set origin */\r
1361                 VectorCopy( sampleOrigin, origin[ b ] );\r
1362                 \r
1363                 /* calculate position */\r
1364                 if( !SubmapRawLuxel( lm, x, y, (bias * biasDirs[ b ][ 0 ]), (bias * biasDirs[ b ][ 1 ]), &cluster[ b ], origin[ b ], normal[ b ] ) )\r
1365                 {\r
1366                         cluster[ b ] = -1;\r
1367                         continue;\r
1368                 }\r
1369                 mapped++;\r
1370                 \r
1371                 /* increment sample count */\r
1372                 luxel[ b ][ 3 ] = lightLuxel[ 3 ] + 1.0f;\r
1373                 \r
1374                 /* setup trace */\r
1375                 trace->cluster = *cluster;\r
1376                 VectorCopy( origin[ b ], trace->origin );\r
1377                 VectorCopy( normal[ b ], trace->normal );\r
1378                 \r
1379                 /* sample light */\r
1380                 //%     LightContributionToSample( light, cluster[ b ], origin[ b ], normal[ b ], luxel[ b ], qtrue, qfalse, lm->numLightSurfaces, &lightSurfaces[ lm->firstLightSurface ] );\r
1381                 LightContributionToSample( trace );\r
1382                 \r
1383                 /* add to totals (fixme: make contrast function) */\r
1384                 VectorCopy( trace->color, luxel[ b ] );\r
1385                 VectorAdd( total, trace->color, total );\r
1386                 if( (luxel[ b ][ 0 ] + luxel[ b ][ 1 ] + luxel[ b ][ 2 ]) > 0.0f )\r
1387                         lighted++;\r
1388         }\r
1389         \r
1390         /* subsample further? */\r
1391         if( (lightLuxel[ 3 ] + 1.0f) < lightSamples &&\r
1392                 (total[ 0 ] > 4.0f || total[ 1 ] > 4.0f || total[ 2 ] > 4.0f) &&\r
1393                 lighted != 0 && lighted != mapped )\r
1394         {\r
1395                 for( b = 0; b < 4; b++ )\r
1396                 {\r
1397                         if( cluster[ b ] < 0 )\r
1398                                 continue;\r
1399                         SubsampleRawLuxel_r( lm, trace, origin[ b ], x, y, (bias * 0.25f), luxel[ b ] );\r
1400                 }\r
1401         }\r
1402         \r
1403         /* average */\r
1404         //%     VectorClear( color );\r
1405         //%     samples = 0;\r
1406         VectorCopy( lightLuxel, color );\r
1407         samples = 1;\r
1408         for( b = 0; b < 4; b++ )\r
1409         {\r
1410                 if( cluster[ b ] < 0 )\r
1411                         continue;\r
1412                 VectorAdd( color, luxel[ b ], color );\r
1413                 samples++;\r
1414         }\r
1415         \r
1416         /* add to luxel */\r
1417         if( samples > 0 )\r
1418         {\r
1419                 /* average */\r
1420                 color[ 0 ] /= samples;\r
1421                 color[ 1 ] /= samples;\r
1422                 color[ 2 ] /= samples;\r
1423                 \r
1424                 /* add to color */\r
1425                 VectorCopy( color, lightLuxel );\r
1426                 lightLuxel[ 3 ] += 1.0f;\r
1427         }\r
1428 }\r
1429 \r
1430 \r
1431 \r
1432 /*\r
1433 IlluminateRawLightmap()\r
1434 illuminates the luxels\r
1435 */\r
1436 \r
1437 #define LIGHT_LUXEL( x, y )             (lightLuxels + ((((y) * lm->sw) + (x)) * SUPER_LUXEL_SIZE))\r
1438 \r
1439 void IlluminateRawLightmap( int rawLightmapNum )\r
1440 {\r
1441         int                                     i, t, x, y, sx, sy, size, llSize, luxelFilterRadius, lightmapNum;\r
1442         int                                     *cluster, *cluster2, mapped, lighted, totalLighted;\r
1443         rawLightmap_t           *lm;\r
1444         surfaceInfo_t           *info;\r
1445         qboolean                        filterColor, filterDir;\r
1446         float                           brightness;\r
1447         float                           *origin, *normal, *luxel, *luxel2, *deluxel, *deluxel2;\r
1448         float                           *lightLuxels, *lightLuxel, samples, filterRadius, weight;\r
1449         vec3_t                          color, averageColor, averageDir, total, temp, temp2;\r
1450         float                           tests[ 4 ][ 2 ] = { { 0.0f, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };\r
1451         trace_t                         trace;\r
1452         \r
1453         \r
1454         /* bail if this number exceeds the number of raw lightmaps */\r
1455         if( rawLightmapNum >= numRawLightmaps )\r
1456                 return;\r
1457         \r
1458         /* get lightmap */\r
1459         lm = &rawLightmaps[ rawLightmapNum ];\r
1460         \r
1461         /* setup trace */\r
1462         trace.testOcclusion = !noTrace;\r
1463         trace.forceSunlight = qfalse;\r
1464         trace.recvShadows = lm->recvShadows;\r
1465         trace.numSurfaces = lm->numLightSurfaces;\r
1466         trace.surfaces = &lightSurfaces[ lm->firstLightSurface ];\r
1467         trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;\r
1468         \r
1469         /* twosided lighting (may or may not be a good idea for lightmapped stuff) */\r
1470         trace.twoSided = qfalse;\r
1471         for( i = 0; i < trace.numSurfaces; i++ )\r
1472         {\r
1473                 /* get surface */\r
1474                 info = &surfaceInfos[ trace.surfaces[ i ] ];\r
1475                 \r
1476                 /* check twosidedness */\r
1477                 if( info->si->twoSided )\r
1478                 {\r
1479                         trace.twoSided = qtrue;\r
1480                         break;\r
1481                 }\r
1482         }\r
1483         \r
1484         /* create a culled light list for this raw lightmap */\r
1485         CreateTraceLightsForBounds( lm->mins, lm->maxs, lm->plane, lm->numLightClusters, lm->lightClusters, LIGHT_SURFACES, &trace );\r
1486         \r
1487         /* -----------------------------------------------------------------\r
1488            fill pass\r
1489            ----------------------------------------------------------------- */\r
1490         \r
1491         /* test debugging state */\r
1492         if( debugSurfaces || debugAxis || debugCluster || debugOrigin || normalmap )\r
1493         {\r
1494                 /* debug fill the luxels */\r
1495                 for( y = 0; y < lm->sh; y++ )\r
1496                 {\r
1497                         for( x = 0; x < lm->sw; x++ )\r
1498                         {\r
1499                                 /* get cluster */\r
1500                                 cluster = SUPER_CLUSTER( x, y );\r
1501 \r
1502                                 /* only fill mapped luxels */\r
1503                                 if( *cluster < 0 )\r
1504                                         continue;\r
1505                                 \r
1506                                 /* get particulars */\r
1507                                 luxel = SUPER_LUXEL( 0, x, y );\r
1508                                 origin = SUPER_ORIGIN( x, y );\r
1509                                 normal = SUPER_NORMAL( x, y );\r
1510                                 \r
1511                                 /* color the luxel with raw lightmap num? */\r
1512                                 if( debugSurfaces )\r
1513                                         VectorCopy( debugColors[ rawLightmapNum % 12 ], luxel );\r
1514                                 \r
1515                                 /* color the luxel with lightmap axis? */\r
1516                                 else if( debugAxis )\r
1517                                 {\r
1518                                         luxel[ 0 ] = (lm->axis[ 0 ] + 1.0f) * 127.5f;\r
1519                                         luxel[ 1 ] = (lm->axis[ 1 ] + 1.0f) * 127.5f;\r
1520                                         luxel[ 2 ] = (lm->axis[ 2 ] + 1.0f) * 127.5f;\r
1521                                 }\r
1522                                 \r
1523                                 /* color the luxel with luxel cluster? */\r
1524                                 else if( debugCluster )\r
1525                                         VectorCopy( debugColors[ *cluster % 12 ], luxel );\r
1526                                 \r
1527                                 /* color the luxel with luxel origin? */\r
1528                                 else if( debugOrigin )\r
1529                                 {\r
1530                                         VectorSubtract( lm->maxs, lm->mins, temp );\r
1531                                         VectorScale( temp, (1.0f / 255.0f), temp );\r
1532                                         VectorSubtract( origin, lm->mins, temp2 );\r
1533                                         luxel[ 0 ] = lm->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);\r
1534                                         luxel[ 1 ] = lm->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);\r
1535                                         luxel[ 2 ] = lm->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);\r
1536                                 }\r
1537                                 \r
1538                                 /* color the luxel with the normal */\r
1539                                 else if( normalmap )\r
1540                                 {\r
1541                                         luxel[ 0 ] = (normal[ 0 ] + 1.0f) * 127.5f;\r
1542                                         luxel[ 1 ] = (normal[ 1 ] + 1.0f) * 127.5f;\r
1543                                         luxel[ 2 ] = (normal[ 2 ] + 1.0f) * 127.5f;\r
1544                                 }\r
1545                                 \r
1546                                 /* add to counts */\r
1547                                 numLuxelsIlluminated++;\r
1548                                 luxel[ 3 ] = 1.0f;\r
1549                         }\r
1550                 }\r
1551         }\r
1552         else\r
1553         {\r
1554                 /* allocate temporary per-light luxel storage */\r
1555                 llSize = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );\r
1556                 lightLuxels = safe_malloc( llSize );\r
1557                 \r
1558                 /* clear luxels */\r
1559                 //%     memset( lm->superLuxels[ 0 ], 0, llSize );\r
1560                 \r
1561                 /* set ambient color */\r
1562                 for( y = 0; y < lm->sh; y++ )\r
1563                 {\r
1564                         for( x = 0; x < lm->sw; x++ )\r
1565                         {\r
1566                                 /* get cluster */\r
1567                                 cluster = SUPER_CLUSTER( x, y );\r
1568                                 luxel = SUPER_LUXEL( 0, x, y );\r
1569                                 normal = SUPER_NORMAL( x, y );\r
1570                                 deluxel = SUPER_DELUXEL( x, y );\r
1571                                 \r
1572                                 /* blacken unmapped clusters */\r
1573                                 if( *cluster < 0 )\r
1574                                         VectorClear( luxel );\r
1575                                 \r
1576                                 /* set ambient */\r
1577                                 else\r
1578                                 {\r
1579                                         VectorCopy( ambientColor, luxel );\r
1580                                         if( deluxemap )\r
1581                                                 VectorScale( normal, 0.00390625f, deluxel );\r
1582                                         luxel[ 3 ] = 1.0f;\r
1583                                 }\r
1584                         }\r
1585                 }\r
1586                 \r
1587                 /* clear styled lightmaps */\r
1588                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );\r
1589                 for( lightmapNum = 1; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
1590                 {\r
1591                         if( lm->superLuxels[ lightmapNum ] != NULL )\r
1592                                 memset( lm->superLuxels[ lightmapNum ], 0, size );\r
1593                 }\r
1594                 \r
1595                 /* debugging code */\r
1596                 //%     if( trace.numLights <= 0 )\r
1597                 //%             Sys_Printf( "Lightmap %9d: 0 lights, axis: %.2f, %.2f, %.2f\n", rawLightmapNum, lm->axis[ 0 ], lm->axis[ 1 ], lm->axis[ 2 ] );\r
1598                 \r
1599                 /* walk light list */\r
1600                 for( i = 0; i < trace.numLights; i++ )\r
1601                 {\r
1602                         /* setup trace */\r
1603                         trace.light = trace.lights[ i ];\r
1604                         \r
1605                         /* style check */\r
1606                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
1607                         {\r
1608                                 if( lm->styles[ lightmapNum ] == trace.light->style ||\r
1609                                         lm->styles[ lightmapNum ] == LS_NONE )\r
1610                                         break;\r
1611                         }\r
1612                         \r
1613                         /* max of MAX_LIGHTMAPS (4) styles allowed to hit a surface/lightmap */\r
1614                         if( lightmapNum >= MAX_LIGHTMAPS )\r
1615                         {\r
1616                                 Sys_Printf( "WARNING: Hit per-surface style limit (%d)\n", MAX_LIGHTMAPS );\r
1617                                 continue;\r
1618                         }\r
1619                         \r
1620                         /* setup */\r
1621                         memset( lightLuxels, 0, llSize );\r
1622                         totalLighted = 0;\r
1623                         \r
1624                         /* initial pass, one sample per luxel */\r
1625                         for( y = 0; y < lm->sh; y++ )\r
1626                         {\r
1627                                 for( x = 0; x < lm->sw; x++ )\r
1628                                 {\r
1629                                         /* get cluster */\r
1630                                         cluster = SUPER_CLUSTER( x, y );\r
1631                                         if( *cluster < 0 )\r
1632                                                 continue;\r
1633                                         \r
1634                                         /* get particulars */\r
1635                                         lightLuxel = LIGHT_LUXEL( x, y );\r
1636                                         deluxel = SUPER_DELUXEL( x, y );\r
1637                                         origin = SUPER_ORIGIN( x, y );\r
1638                                         normal = SUPER_NORMAL( x, y );\r
1639                                         \r
1640                                         /* set contribution count */\r
1641                                         lightLuxel[ 3 ] = 1.0f;\r
1642                                         \r
1643                                         /* setup trace */\r
1644                                         trace.cluster = *cluster;\r
1645                                         VectorCopy( origin, trace.origin );\r
1646                                         VectorCopy( normal, trace.normal );\r
1647                                         \r
1648                                         /* get light for this sample */\r
1649                                         LightContributionToSample( &trace );\r
1650                                         VectorCopy( trace.color, lightLuxel );\r
1651                                         \r
1652                                         /* add to count */\r
1653                                         if( trace.color[ 0 ] || trace.color[ 1 ] || trace.color[ 2 ] )\r
1654                                                 totalLighted++;\r
1655                                         \r
1656                                         /* add to light direction map (fixme: use luxel normal as starting point for deluxel?) */\r
1657                                         if( deluxemap )\r
1658                                         {\r
1659                                                 /* color to grayscale (photoshop rgb weighting) */\r
1660                                                 brightness = trace.color[ 0 ] * 0.3f + trace.color[ 1 ] * 0.59f + trace.color[ 2 ] * 0.11f;\r
1661                                                 brightness *= (1.0 / 255.0);\r
1662                                                 VectorScale( trace.direction, brightness, trace.direction );\r
1663                                                 VectorAdd( deluxel, trace.direction, deluxel );\r
1664                                         }\r
1665                                 }\r
1666                         }\r
1667                         \r
1668                         /* don't even bother with everything else if nothing was lit */\r
1669                         if( totalLighted == 0 )\r
1670                                 continue;\r
1671                         \r
1672                         /* determine filter radius */\r
1673                         filterRadius = lm->filterRadius > trace.light->filterRadius\r
1674                                 ? lm->filterRadius\r
1675                                 : trace.light->filterRadius;\r
1676                         if( filterRadius < 0.0f )\r
1677                                 filterRadius = 0.0f;\r
1678                         \r
1679                         /* set luxel filter radius */\r
1680                         luxelFilterRadius = superSample * filterRadius / lm->sampleSize;\r
1681                         if( luxelFilterRadius == 0 && (filterRadius > 0.0f || filter) )\r
1682                                 luxelFilterRadius = 1;\r
1683                         \r
1684                         /* secondary pass, adaptive supersampling (fixme: use a contrast function to determine if subsampling is necessary) */\r
1685                         /* 2003-09-27: changed it so filtering disamples supersampling, as it would waste time */\r
1686                         if( lightSamples > 1 && luxelFilterRadius == 0 )\r
1687                         {\r
1688                                 /* walk luxels */\r
1689                                 for( y = 0; y < (lm->sh - 1); y++ )\r
1690                                 {\r
1691                                         for( x = 0; x < (lm->sw - 1); x++ )\r
1692                                         {\r
1693                                                 /* setup */\r
1694                                                 mapped = 0;\r
1695                                                 lighted = 0;\r
1696                                                 VectorClear( total );\r
1697                                                 \r
1698                                                 /* test 2x2 stamp */\r
1699                                                 for( t = 0; t < 4; t++ )\r
1700                                                 {\r
1701                                                         /* set sample coords */\r
1702                                                         sx = x + tests[ t ][ 0 ];\r
1703                                                         sy = y + tests[ t ][ 1 ];\r
1704                                                         \r
1705                                                         /* get cluster */\r
1706                                                         cluster = SUPER_CLUSTER( sx, sy );\r
1707                                                         if( *cluster < 0 )\r
1708                                                                 continue;\r
1709                                                         mapped++;\r
1710                                                         \r
1711                                                         /* get luxel */\r
1712                                                         lightLuxel = LIGHT_LUXEL( sx, sy );\r
1713                                                         VectorAdd( total, lightLuxel, total );\r
1714                                                         if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) > 0.0f )\r
1715                                                                 lighted++;\r
1716                                                 }\r
1717                                                 \r
1718                                                 /* if total color is under a certain amount, then don't bother subsampling */\r
1719                                                 if( total[ 0 ] <= 4.0f && total[ 1 ] <= 4.0f && total[ 2 ] <= 4.0f )\r
1720                                                         continue;\r
1721                                                 \r
1722                                                 /* if all 4 pixels are either in shadow or light, then don't subsample */\r
1723                                                 if( lighted != 0 && lighted != mapped )\r
1724                                                 {\r
1725                                                         for( t = 0; t < 4; t++ )\r
1726                                                         {\r
1727                                                                 /* set sample coords */\r
1728                                                                 sx = x + tests[ t ][ 0 ];\r
1729                                                                 sy = y + tests[ t ][ 1 ];\r
1730                                                                 \r
1731                                                                 /* get luxel */\r
1732                                                                 cluster = SUPER_CLUSTER( sx, sy );\r
1733                                                                 if( *cluster < 0 )\r
1734                                                                         continue;\r
1735                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );\r
1736                                                                 origin = SUPER_ORIGIN( sx, sy );\r
1737                                                                 \r
1738                                                                 /* only subsample shadowed luxels */\r
1739                                                                 //%     if( (lightLuxel[ 0 ] + lightLuxel[ 1 ] + lightLuxel[ 2 ]) <= 0.0f )\r
1740                                                                 //%             continue;\r
1741                                                                 \r
1742                                                                 /* subsample it */\r
1743                                                                 SubsampleRawLuxel_r( lm, &trace, origin, sx, sy, 0.25f, lightLuxel );\r
1744                                                                 \r
1745                                                                 /* debug code to colorize subsampled areas to yellow */\r
1746                                                                 //%     luxel = SUPER_LUXEL( lightmapNum, sx, sy );\r
1747                                                                 //%     VectorSet( luxel, 255, 204, 0 );\r
1748                                                         }\r
1749                                                 }\r
1750                                         }\r
1751                                 }\r
1752                         }\r
1753                         \r
1754                         /* allocate sampling lightmap storage */\r
1755                         if( lm->superLuxels[ lightmapNum ] == NULL )\r
1756                         {\r
1757                                 /* allocate sampling lightmap storage */\r
1758                                 size = lm->sw * lm->sh * SUPER_LUXEL_SIZE * sizeof( float );\r
1759                                 lm->superLuxels[ lightmapNum ] = safe_malloc( size );\r
1760                                 memset( lm->superLuxels[ lightmapNum ], 0, size );\r
1761                         }\r
1762                         \r
1763                         /* set style */\r
1764                         if( lightmapNum > 0 )\r
1765                         {\r
1766                                 lm->styles[ lightmapNum ] = trace.light->style;\r
1767                                 //%     Sys_Printf( "Surface %6d has lightstyle %d\n", rawLightmapNum, trace.light->style );\r
1768                         }\r
1769                         \r
1770                         /* copy to permanent luxels */\r
1771                         for( y = 0; y < lm->sh; y++ )\r
1772                         {\r
1773                                 for( x = 0; x < lm->sw; x++ )\r
1774                                 {\r
1775                                         /* get cluster and origin */\r
1776                                         cluster = SUPER_CLUSTER( x, y );\r
1777                                         if( *cluster < 0 )\r
1778                                                 continue;\r
1779                                         origin = SUPER_ORIGIN( x, y );\r
1780                                         \r
1781                                         /* filter? */\r
1782                                         if( luxelFilterRadius )\r
1783                                         {\r
1784                                                 /* setup */\r
1785                                                 VectorClear( averageColor );\r
1786                                                 samples = 0.0f;\r
1787                                                 \r
1788                                                 /* cheaper distance-based filtering */\r
1789                                                 for( sy = (y - luxelFilterRadius); sy <= (y + luxelFilterRadius); sy++ )\r
1790                                                 {\r
1791                                                         if( sy < 0 || sy >= lm->sh )\r
1792                                                                 continue;\r
1793                                                         \r
1794                                                         for( sx = (x - luxelFilterRadius); sx <= (x + luxelFilterRadius); sx++ )\r
1795                                                         {\r
1796                                                                 if( sx < 0 || sx >= lm->sw )\r
1797                                                                         continue;\r
1798                                                                 \r
1799                                                                 /* get particulars */\r
1800                                                                 cluster = SUPER_CLUSTER( sx, sy );\r
1801                                                                 if( *cluster < 0 )\r
1802                                                                         continue;\r
1803                                                                 lightLuxel = LIGHT_LUXEL( sx, sy );\r
1804                                                                 \r
1805                                                                 /* create weight */\r
1806                                                                 weight = (abs( sx - x ) == luxelFilterRadius ? 0.5f : 1.0f);\r
1807                                                                 weight *= (abs( sy - y ) == luxelFilterRadius ? 0.5f : 1.0f);\r
1808                                                                 \r
1809                                                                 /* scale luxel by filter weight */\r
1810                                                                 VectorScale( lightLuxel, weight, color );\r
1811                                                                 VectorAdd( averageColor, color, averageColor );\r
1812                                                                 samples += weight;\r
1813                                                         }\r
1814                                                 }\r
1815                                                 \r
1816                                                 /* any samples? */\r
1817                                                 if( samples <= 0.0f     )\r
1818                                                         continue;\r
1819                                                 \r
1820                                                 /* scale into luxel */\r
1821                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );\r
1822                                                 luxel[ 3 ] = 1.0f;\r
1823                                                 \r
1824                                                 /* handle negative light */\r
1825                                                 if( trace.light->flags & LIGHT_NEGATIVE )\r
1826                                                 { \r
1827                                                         luxel[ 0 ] -= averageColor[ 0 ] / samples;\r
1828                                                         luxel[ 1 ] -= averageColor[ 1 ] / samples;\r
1829                                                         luxel[ 2 ] -= averageColor[ 2 ] / samples;\r
1830                                                 }\r
1831                                                 \r
1832                                                 /* handle normal light */\r
1833                                                 else\r
1834                                                 { \r
1835                                                         luxel[ 0 ] += averageColor[ 0 ] / samples;\r
1836                                                         luxel[ 1 ] += averageColor[ 1 ] / samples;\r
1837                                                         luxel[ 2 ] += averageColor[ 2 ] / samples;\r
1838                                                 }\r
1839                                         }\r
1840                                         \r
1841                                         /* single sample */\r
1842                                         else\r
1843                                         {\r
1844                                                 /* get particulars */\r
1845                                                 lightLuxel = LIGHT_LUXEL( x, y );\r
1846                                                 luxel = SUPER_LUXEL( lightmapNum, x, y );\r
1847                                                 \r
1848                                                 /* handle negative light */\r
1849                                                 if( trace.light->flags & LIGHT_NEGATIVE )\r
1850                                                         VectorScale( averageColor, -1.0f, averageColor );\r
1851 \r
1852                                                 /* add color */\r
1853                                                 luxel[ 3 ] = 1.0f;\r
1854                                                 \r
1855                                                 /* handle negative light */\r
1856                                                 if( trace.light->flags & LIGHT_NEGATIVE )\r
1857                                                         VectorSubtract( luxel, lightLuxel, luxel );\r
1858                                                 \r
1859                                                 /* handle normal light */\r
1860                                                 else\r
1861                                                         VectorAdd( luxel, lightLuxel, luxel );\r
1862                                         }\r
1863                                 }\r
1864                         }\r
1865                 }\r
1866                 \r
1867                 /* free temporary luxels */\r
1868                 free( lightLuxels );\r
1869         }\r
1870         \r
1871         /* free light list */\r
1872         FreeTraceLights( &trace );\r
1873         \r
1874         /* -----------------------------------------------------------------\r
1875            filter pass\r
1876            ----------------------------------------------------------------- */\r
1877         \r
1878         /* walk lightmaps */\r
1879         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
1880         {\r
1881                 /* early out */\r
1882                 if( lm->superLuxels[ lightmapNum ] == NULL )\r
1883                         continue;\r
1884                 \r
1885                 /* average occluded luxels from neighbors */\r
1886                 for( y = 0; y < lm->sh; y++ )\r
1887                 {\r
1888                         for( x = 0; x < lm->sw; x++ )\r
1889                         {\r
1890                                 /* get particulars */\r
1891                                 cluster = SUPER_CLUSTER( x, y );\r
1892                                 luxel = SUPER_LUXEL( lightmapNum, x, y );\r
1893                                 deluxel = SUPER_DELUXEL( x, y );\r
1894                                 normal = SUPER_NORMAL( x, y );\r
1895                                 \r
1896                                 /* determine if filtering is necessary */\r
1897                                 filterColor = qfalse;\r
1898                                 filterDir = qfalse;\r
1899                                 if( *cluster < 0 ||\r
1900                                         (lm->splotchFix && (luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ])) )\r
1901                                         filterColor = qtrue;\r
1902                                 if( deluxemap && lightmapNum == 0 && (*cluster < 0 || filter) )\r
1903                                         filterDir = qtrue;\r
1904                                 \r
1905                                 if( !filterColor && !filterDir )\r
1906                                         continue;\r
1907                                 \r
1908                                 /* choose seed amount */\r
1909                                 VectorClear( averageColor );\r
1910                                 VectorClear( averageDir );\r
1911                                 samples = 0;\r
1912                                 \r
1913                                 /* walk 3x3 matrix */\r
1914                                 for( sy = (y - 1); sy <= (y + 1); sy++ )\r
1915                                 {\r
1916                                         if( sy < 0 || sy >= lm->sh )\r
1917                                                 continue;\r
1918                                         \r
1919                                         for( sx = (x - 1); sx <= (x + 1); sx++ )\r
1920                                         {\r
1921                                                 if( sx < 0 || sx >= lm->sw || (sx == x && sy == y) )\r
1922                                                         continue;\r
1923                                                 \r
1924                                                 /* get neighbor's particulars */\r
1925                                                 cluster2 = SUPER_CLUSTER( sx, sy );\r
1926                                                 luxel2 = SUPER_LUXEL( lightmapNum, sx, sy );\r
1927                                                 deluxel2 = SUPER_DELUXEL( sx, sy );\r
1928                                                 \r
1929                                                 /* ignore unmapped/unlit luxels */\r
1930                                                 if( *cluster2 < 0 || luxel2[ 3 ] == 0.0f ||\r
1931                                                         (lm->splotchFix && VectorCompare( luxel2, ambientColor )) )\r
1932                                                         continue;\r
1933                                                 \r
1934                                                 /* add its distinctiveness to our own */\r
1935                                                 VectorAdd( averageColor, luxel2, averageColor );\r
1936                                                 samples += luxel2[ 3 ];\r
1937                                                 if( filterDir )\r
1938                                                         VectorAdd( averageDir, deluxel2, averageDir );\r
1939                                         }\r
1940                                 }\r
1941                                 \r
1942                                 /* fall through */\r
1943                                 if( samples == 0.0f )\r
1944                                         continue;\r
1945                                 \r
1946                                 /* average it */\r
1947                                 if( filterColor )\r
1948                                 {\r
1949                                         VectorDivide( averageColor, samples, luxel );\r
1950                                         luxel[ 3 ] = 1.0f;\r
1951                                 }\r
1952                                 if( filterDir )\r
1953                                         VectorDivide( averageDir, samples, deluxel );\r
1954                                 \r
1955                                 /* set cluster to -3 */\r
1956                                 if( *cluster < 0 )\r
1957                                         *cluster = CLUSTER_FLOODED;\r
1958                         }\r
1959                 }\r
1960         }\r
1961 }\r
1962 \r
1963 \r
1964 \r
1965 /*\r
1966 IlluminateVertexes()\r
1967 light the surface vertexes\r
1968 */\r
1969 \r
1970 #define VERTEX_NUDGE    2.0f\r
1971 \r
1972 void IlluminateVertexes( int num )\r
1973 {\r
1974         int                                     i, x, y, z, x1, y1, z1, sx, sy, radius, maxRadius, *cluster;\r
1975         int                                     lightmapNum;\r
1976         float                           samples, *vertLuxel, *radVertLuxel, *luxel;\r
1977         vec3_t                          origin, temp, temp2, colors[ MAX_LIGHTMAPS ];\r
1978         bspDrawSurface_t        *ds;\r
1979         surfaceInfo_t           *info;\r
1980         rawLightmap_t           *lm;\r
1981         bspDrawVert_t           *verts;\r
1982         trace_t                         trace;\r
1983         \r
1984         \r
1985         /* der... */\r
1986         if( noVertexLighting )\r
1987                 return;\r
1988         \r
1989         /* get surface, info, and raw lightmap */\r
1990         ds = &bspDrawSurfaces[ num ];\r
1991         info = &surfaceInfos[ num ];\r
1992         lm = info->lm;\r
1993         \r
1994         /* -----------------------------------------------------------------\r
1995            illuminate the vertexes\r
1996            ----------------------------------------------------------------- */\r
1997         \r
1998         /* calculate vertex lighting for surfaces without lightmaps */\r
1999         if( lm == NULL )\r
2000         {\r
2001                 /* setup trace */\r
2002                 trace.testOcclusion = !noTrace;\r
2003                 trace.forceSunlight = info->si->forceSunlight;\r
2004                 trace.recvShadows = info->recvShadows;\r
2005                 trace.numSurfaces = 1;\r
2006                 trace.surfaces = &num;\r
2007                 trace.inhibitRadius = DEFAULT_INHIBIT_RADIUS;\r
2008                 \r
2009                 /* twosided lighting */\r
2010                 trace.twoSided = info->si->twoSided;\r
2011                 \r
2012                 /* make light list for this surface */\r
2013                 CreateTraceLightsForSurface( num, &trace );\r
2014                 \r
2015                 /* walk the surface verts */\r
2016                 verts = yDrawVerts + ds->firstVert;\r
2017                 for( i = 0; i < ds->numVerts; i++ )\r
2018                 {\r
2019                         /* get vertex luxel */\r
2020                         radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );\r
2021                         \r
2022                         /* color the luxel with raw lightmap num? */\r
2023                         if( debugSurfaces )\r
2024                                 VectorCopy( debugColors[ num % 12 ], radVertLuxel );\r
2025                         \r
2026                         /* color the luxel with luxel origin? */\r
2027                         else if( debugOrigin )\r
2028                         {\r
2029                                 VectorSubtract( info->maxs, info->mins, temp );\r
2030                                 VectorScale( temp, (1.0f / 255.0f), temp );\r
2031                                 VectorSubtract( origin, lm->mins, temp2 );\r
2032                                 radVertLuxel[ 0 ] = info->mins[ 0 ] + (temp[ 0 ] * temp2[ 0 ]);\r
2033                                 radVertLuxel[ 1 ] = info->mins[ 1 ] + (temp[ 1 ] * temp2[ 1 ]);\r
2034                                 radVertLuxel[ 2 ] = info->mins[ 2 ] + (temp[ 2 ] * temp2[ 2 ]);\r
2035                         }\r
2036                         \r
2037                         /* color the luxel with the normal */\r
2038                         else if( normalmap )\r
2039                         {\r
2040                                 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;\r
2041                                 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;\r
2042                                 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;\r
2043                         }\r
2044                         \r
2045                         /* illuminate the vertex */\r
2046                         else\r
2047                         {\r
2048                                 /* clear vertex luxel */\r
2049                                 VectorCopy( ambientColor, radVertLuxel );\r
2050                                 \r
2051                                 /* try at initial origin */\r
2052                                 trace.cluster = ClusterForPointExtFilter( verts[ i ].xyz, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );\r
2053                                 if( trace.cluster >= 0 )\r
2054                                 {\r
2055                                         /* setup trace */\r
2056                                         VectorCopy( verts[ i ].xyz, trace.origin );\r
2057                                         VectorCopy( verts[ i ].normal, trace.normal );\r
2058                                         \r
2059                                         /* trace */\r
2060                                         LightingAtSample( &trace, ds->vertexStyles, colors );\r
2061                                         \r
2062                                         /* store */\r
2063                                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
2064                                         {\r
2065                                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
2066                                                 VectorCopy( colors[ lightmapNum ], radVertLuxel );\r
2067                                         }\r
2068                                 }\r
2069                                 \r
2070                                 /* is this sample bright enough? */\r
2071                                 if( radVertLuxel[ 0 ] <= ambientColor[ 0 ] &&\r
2072                                         radVertLuxel[ 1 ] <= ambientColor[ 1 ] &&\r
2073                                         radVertLuxel[ 2 ] <= ambientColor[ 2 ] )\r
2074                                 {\r
2075                                         /* nudge the sample point around a bit */\r
2076                                         for( x = 0; x < 4; x++ )\r
2077                                         {\r
2078                                                 /* two's complement 0, 1, -1, 2, -2, etc */\r
2079                                                 x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);\r
2080                                                 \r
2081                                                 for( y = 0; y < 4; y++ )\r
2082                                                 {\r
2083                                                         y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);\r
2084                                                         \r
2085                                                         for( z = 0; z < 4; z++ )\r
2086                                                         {\r
2087                                                                 z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);\r
2088                                                                 \r
2089                                                                 /* nudge origin */\r
2090                                                                 trace.origin[ 0 ] = verts[ i ].xyz[ 0 ] + (VERTEX_NUDGE * x1);\r
2091                                                                 trace.origin[ 1 ] = verts[ i ].xyz[ 1 ] + (VERTEX_NUDGE * y1);\r
2092                                                                 trace.origin[ 2 ] = verts[ i ].xyz[ 2 ] + (VERTEX_NUDGE * z1);\r
2093                                                                 \r
2094                                                                 /* try at nudged origin */\r
2095                                                                 trace.cluster = ClusterForPointExtFilter( origin, VERTEX_EPSILON, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ] );\r
2096                                                                 if( trace.cluster < 0 )\r
2097                                                                         continue;\r
2098                                                                                                                         \r
2099                                                                 /* trace */\r
2100                                                                 LightingAtSample( &trace, ds->vertexStyles, colors );\r
2101                                                                 \r
2102                                                                 /* store */\r
2103                                                                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
2104                                                                 {\r
2105                                                                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
2106                                                                         VectorCopy( colors[ lightmapNum ], radVertLuxel );\r
2107                                                                 }\r
2108                                                                 \r
2109                                                                 /* bright enough? */\r
2110                                                                 radVertLuxel = RAD_VERTEX_LUXEL( 0, ds->firstVert + i );\r
2111                                                                 if( radVertLuxel[ 0 ] > ambientColor[ 0 ] ||\r
2112                                                                         radVertLuxel[ 1 ] > ambientColor[ 1 ] ||\r
2113                                                                         radVertLuxel[ 2 ] > ambientColor[ 2 ] )\r
2114                                                                         x = y = z = 1000;\r
2115                                                         }\r
2116                                                 }\r
2117                                         }\r
2118                                 }\r
2119                         }\r
2120                         \r
2121                         /* another happy customer */\r
2122                         numVertsIlluminated++;\r
2123                         \r
2124                         /* store it */\r
2125                         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
2126                         {\r
2127                                 /* get luxels */\r
2128                                 vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
2129                                 radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
2130                                 \r
2131                                 /* store */\r
2132                                 if( bouncing || bounce == 0 || !bounceOnly )\r
2133                                         VectorAdd( vertLuxel, radVertLuxel, vertLuxel );\r
2134                                 ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], info->si->vertexScale );\r
2135                         }\r
2136                 }\r
2137                 \r
2138                 /* free light list */\r
2139                 FreeTraceLights( &trace );\r
2140                 \r
2141                 /* return to sender */\r
2142                 return;\r
2143         }\r
2144         \r
2145         /* -----------------------------------------------------------------\r
2146            reconstitute vertex lighting from the luxels\r
2147            ----------------------------------------------------------------- */\r
2148         \r
2149         /* set styles from lightmap */\r
2150         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
2151                 ds->vertexStyles[ lightmapNum ] = lm->styles[ lightmapNum ];\r
2152         \r
2153         /* get max search radius */\r
2154         maxRadius = lm->sw;\r
2155         maxRadius = maxRadius > lm->sh ? maxRadius : lm->sh;\r
2156         \r
2157         /* walk the surface verts */\r
2158         verts = yDrawVerts + ds->firstVert;\r
2159         for( i = 0; i < ds->numVerts; i++ )\r
2160         {\r
2161                 /* do each lightmap */\r
2162                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
2163                 {\r
2164                         /* early out */\r
2165                         if( lm->superLuxels[ lightmapNum ] == NULL )\r
2166                                 continue;\r
2167                         \r
2168                         /* get luxel coords */\r
2169                         x = verts[ i ].lightmap[ lightmapNum ][ 0 ];\r
2170                         y = verts[ i ].lightmap[ lightmapNum ][ 1 ];\r
2171                         if( x < 0 )\r
2172                                 x = 0;\r
2173                         else if( x >= lm->sw )\r
2174                                 x = lm->sw - 1;\r
2175                         if( y < 0 )\r
2176                                 y = 0;\r
2177                         else if( y >= lm->sh )\r
2178                                 y = lm->sh - 1;\r
2179                         \r
2180                         /* get vertex luxels */\r
2181                         vertLuxel = VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
2182                         radVertLuxel = RAD_VERTEX_LUXEL( lightmapNum, ds->firstVert + i );\r
2183                         \r
2184                         /* color the luxel with the normal? */\r
2185                         if( normalmap )\r
2186                         {\r
2187                                 radVertLuxel[ 0 ] = (verts[ i ].normal[ 0 ] + 1.0f) * 127.5f;\r
2188                                 radVertLuxel[ 1 ] = (verts[ i ].normal[ 1 ] + 1.0f) * 127.5f;\r
2189                                 radVertLuxel[ 2 ] = (verts[ i ].normal[ 2 ] + 1.0f) * 127.5f;\r
2190                         }\r
2191                         \r
2192                         /* color the luxel with surface num? */\r
2193                         else if( debugSurfaces )\r
2194                                 VectorCopy( debugColors[ num % 12 ], radVertLuxel );\r
2195                         \r
2196                         /* divine color from the superluxels */\r
2197                         else\r
2198                         {\r
2199                                 /* increasing radius */\r
2200                                 VectorClear( radVertLuxel );\r
2201                                 samples = 0.0f;\r
2202                                 for( radius = 0; radius < maxRadius && samples <= 0.0f; radius++ )\r
2203                                 {\r
2204                                         /* sample within radius */\r
2205                                         for( sy = (y - radius); sy <= (y + radius); sy++ )\r
2206                                         {\r
2207                                                 if( sy < 0 || sy >= lm->sh )\r
2208                                                         continue;\r
2209                                                 \r
2210                                                 for( sx = (x - radius); sx <= (x + radius); sx++ )\r
2211                                                 {\r
2212                                                         if( sx < 0 || sx >= lm->sw )\r
2213                                                                 continue;\r
2214                                                         \r
2215                                                         /* get luxel particulars */\r
2216                                                         luxel = SUPER_LUXEL( lightmapNum, sx, sy );\r
2217                                                         cluster = SUPER_CLUSTER( sx, sy );\r
2218                                                         if( *cluster < 0 )\r
2219                                                                 continue;\r
2220                                                         \r
2221                                                         /* testing: must be brigher than ambient color */\r
2222                                                         //%     if( luxel[ 0 ] <= ambientColor[ 0 ] || luxel[ 1 ] <= ambientColor[ 1 ] || luxel[ 2 ] <= ambientColor[ 2 ] )\r
2223                                                         //%             continue;\r
2224                                                         \r
2225                                                         /* add its distinctiveness to our own */\r
2226                                                         VectorAdd( radVertLuxel, luxel, radVertLuxel );\r
2227                                                         samples += luxel[ 3 ];\r
2228                                                 }\r
2229                                         }\r
2230                                 }\r
2231                                 \r
2232                                 /* any color? */\r
2233                                 if( samples > 0.0f )\r
2234                                         VectorDivide( radVertLuxel, samples, radVertLuxel );\r
2235                                 else\r
2236                                         VectorCopy( ambientColor, radVertLuxel );\r
2237                         }\r
2238                         \r
2239                         /* store into floating point storage */\r
2240                         VectorAdd( vertLuxel, radVertLuxel, vertLuxel );\r
2241                         numVertsIlluminated++;\r
2242                         \r
2243                         /* store into bytes (for vertex approximation) */\r
2244                         ColorToBytes( vertLuxel, verts[ i ].color[ lightmapNum ], 1.0f );\r
2245                 }\r
2246         }\r
2247 }\r
2248 \r
2249 \r
2250 \r
2251 /* -------------------------------------------------------------------------------\r
2252 \r
2253 light optimization (-fast)\r
2254 \r
2255 creates a list of lights that will affect a surface and stores it in tw\r
2256 this is to optimize surface lighting by culling out as many of the\r
2257 lights in the world as possible from further calculation\r
2258 \r
2259 ------------------------------------------------------------------------------- */\r
2260 \r
2261 /*\r
2262 SetupBrushes()\r
2263 determines opaque brushes in the world and find sky shaders for sunlight calculations\r
2264 */\r
2265 \r
2266 void SetupBrushes( void )\r
2267 {\r
2268         int                             i, j, b, compileFlags;\r
2269         qboolean                inside;\r
2270         bspBrush_t              *brush;\r
2271         bspBrushSide_t  *side;\r
2272         bspShader_t             *shader;\r
2273         shaderInfo_t    *si;\r
2274         \r
2275         \r
2276         /* note it */\r
2277         Sys_FPrintf( SYS_VRB, "--- SetupBrushes ---\n" );\r
2278         \r
2279         /* allocate */\r
2280         if( opaqueBrushes == NULL )\r
2281                 opaqueBrushes = safe_malloc( numBSPBrushes / 8 + 1 );\r
2282         \r
2283         /* clear */\r
2284         memset( opaqueBrushes, 0, numBSPBrushes / 8 + 1 );\r
2285         numOpaqueBrushes = 0;\r
2286         \r
2287         /* walk the list of worldspawn brushes */\r
2288         for( i = 0; i < bspModels[ 0 ].numBSPBrushes; i++ )\r
2289         {\r
2290                 /* get brush */\r
2291                 b = bspModels[ 0 ].firstBSPBrush + i;\r
2292                 brush = &bspBrushes[ b ];\r
2293                 \r
2294                 /* check all sides */\r
2295                 inside = qtrue;\r
2296                 compileFlags = 0;\r
2297                 for( j = 0; j < brush->numSides && inside; j++ )\r
2298                 {\r
2299                         /* do bsp shader calculations */\r
2300                         side = &bspBrushSides[ brush->firstSide + j ];\r
2301                         shader = &bspShaders[ side->shaderNum ];\r
2302                         \r
2303                         /* get shader info */\r
2304                         si = ShaderInfoForShader( shader->shader );\r
2305                         if( si == NULL )\r
2306                                 continue;\r
2307                         \r
2308                         /* or together compile flags */\r
2309                         compileFlags |= si->compileFlags;\r
2310                 }\r
2311                 \r
2312                 /* determine if this brush is opaque to light */\r
2313                 if( !(compileFlags & C_TRANSLUCENT) )\r
2314                 {\r
2315                         opaqueBrushes[ b >> 3 ] |= (1 << (b & 7));\r
2316                         numOpaqueBrushes++;\r
2317                         maxOpaqueBrush = i;\r
2318                 }\r
2319         }\r
2320         \r
2321         /* emit some statistics */\r
2322         Sys_FPrintf( SYS_VRB, "%9d opaque brushes\n", numOpaqueBrushes );\r
2323 }\r
2324 \r
2325 \r
2326 \r
2327 /*\r
2328 ClusterVisible()\r
2329 determines if two clusters are visible to each other using the PVS\r
2330 */\r
2331 \r
2332 qboolean ClusterVisible( int a, int b )\r
2333 {\r
2334         int                     portalClusters, leafBytes;\r
2335         byte            *pvs;\r
2336         \r
2337         \r
2338         /* dummy check */\r
2339         if( a < 0 || b < 0 )\r
2340                 return qfalse;\r
2341         \r
2342         /* early out */\r
2343         if( a == b )\r
2344                 return qtrue;\r
2345         \r
2346         /* not vised? */\r
2347         if( numBSPVisBytes <=8 )\r
2348                 return qtrue;\r
2349         \r
2350         /* get pvs data */\r
2351         portalClusters = ((int *) bspVisBytes)[ 0 ];\r
2352         leafBytes = ((int*) bspVisBytes)[ 1 ];\r
2353         pvs = bspVisBytes + VIS_HEADER_SIZE + (a * leafBytes);\r
2354         \r
2355         /* check */\r
2356         if( (pvs[ b >> 3 ] & (1 << (b & 7))) )\r
2357                 return qtrue;\r
2358         return qfalse;\r
2359 }\r
2360 \r
2361 \r
2362 \r
2363 /*\r
2364 PointInLeafNum_r()\r
2365 borrowed from vlight.c\r
2366 */\r
2367 \r
2368 int     PointInLeafNum_r( vec3_t point, int nodenum )\r
2369 {\r
2370         int                     leafnum;\r
2371         vec_t           dist;\r
2372         bspNode_t               *node;\r
2373         bspPlane_t      *plane;\r
2374         \r
2375         \r
2376         while( nodenum >= 0 )\r
2377         {\r
2378                 node = &bspNodes[ nodenum ];\r
2379                 plane = &bspPlanes[ node->planeNum ];\r
2380                 dist = DotProduct( point, plane->normal ) - plane->dist;\r
2381                 if( dist > 0.1 )\r
2382                         nodenum = node->children[ 0 ];\r
2383                 else if( dist < -0.1 )\r
2384                         nodenum = node->children[ 1 ];\r
2385                 else\r
2386                 {\r
2387                         leafnum = PointInLeafNum_r( point, node->children[ 0 ] );\r
2388                         if( bspLeafs[ leafnum ].cluster != -1 )\r
2389                                 return leafnum;\r
2390                         nodenum = node->children[ 1 ];\r
2391                 }\r
2392         }\r
2393         \r
2394         leafnum = -nodenum - 1;\r
2395         return leafnum;\r
2396 }\r
2397 \r
2398 \r
2399 \r
2400 /*\r
2401 PointInLeafnum()\r
2402 borrowed from vlight.c\r
2403 */\r
2404 \r
2405 int     PointInLeafNum( vec3_t point )\r
2406 {\r
2407         return PointInLeafNum_r( point, 0 );\r
2408 }\r
2409 \r
2410 \r
2411 \r
2412 /*\r
2413 ClusterVisibleToPoint() - ydnar\r
2414 returns qtrue if point can "see" cluster\r
2415 */\r
2416 \r
2417 qboolean ClusterVisibleToPoint( vec3_t point, int cluster )\r
2418 {\r
2419         int             pointCluster;\r
2420         \r
2421 \r
2422         /* get leafNum for point */\r
2423         pointCluster = ClusterForPoint( point );\r
2424         if( pointCluster < 0 )\r
2425                 return qfalse;\r
2426         \r
2427         /* check pvs */\r
2428         return ClusterVisible( pointCluster, cluster );\r
2429 }\r
2430 \r
2431 \r
2432 \r
2433 /*\r
2434 ClusterForPoint() - ydnar\r
2435 returns the pvs cluster for point\r
2436 */\r
2437 \r
2438 int ClusterForPoint( vec3_t point )\r
2439 {\r
2440         int             leafNum;\r
2441         \r
2442 \r
2443         /* get leafNum for point */\r
2444         leafNum = PointInLeafNum( point );\r
2445         if( leafNum < 0 )\r
2446                 return -1;\r
2447         \r
2448         /* return the cluster */\r
2449         return bspLeafs[ leafNum ].cluster;\r
2450 }\r
2451 \r
2452 \r
2453 \r
2454 /*\r
2455 ClusterForPointExt() - ydnar\r
2456 also takes brushes into account for occlusion testing\r
2457 */\r
2458 \r
2459 int ClusterForPointExt( vec3_t point, float epsilon )\r
2460 {\r
2461         int                             i, j, b, leafNum, cluster;\r
2462         float                   dot;\r
2463         qboolean                inside;\r
2464         int                             *brushes, numBSPBrushes;\r
2465         bspLeaf_t               *leaf;\r
2466         bspBrush_t              *brush;\r
2467         bspPlane_t              *plane;\r
2468         \r
2469         \r
2470         /* get leaf for point */\r
2471         leafNum = PointInLeafNum( point );\r
2472         if( leafNum < 0 )\r
2473                 return -1;\r
2474         leaf = &bspLeafs[ leafNum ];\r
2475         \r
2476         /* get the cluster */\r
2477         cluster = leaf->cluster;\r
2478         if( cluster < 0 )\r
2479                 return -1;\r
2480         \r
2481         /* transparent leaf, so check point against all brushes in the leaf */\r
2482         brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];\r
2483         numBSPBrushes = leaf->numBSPLeafBrushes;\r
2484         for( i = 0; i < numBSPBrushes; i++ )\r
2485         {\r
2486                 /* get parts */\r
2487                 b = brushes[ i ];\r
2488                 if( b > maxOpaqueBrush )\r
2489                         continue;\r
2490                 brush = &bspBrushes[ b ];\r
2491                 if( !(opaqueBrushes[ b >> 3 ] & (1 << (b & 7))) )\r
2492                         continue;\r
2493                 \r
2494                 /* check point against all planes */\r
2495                 inside = qtrue;\r
2496                 for( j = 0; j < brush->numSides && inside; j++ )\r
2497                 {\r
2498                         plane = &bspPlanes[ bspBrushSides[ brush->firstSide + j ].planeNum ];\r
2499                         dot = DotProduct( point, plane->normal );\r
2500                         dot -= plane->dist;\r
2501                         if( dot > epsilon )\r
2502                                 inside = qfalse;\r
2503                 }\r
2504                 \r
2505                 /* if inside, return bogus cluster */\r
2506                 if( inside )\r
2507                         return -1 - b;\r
2508         }\r
2509         \r
2510         /* if the point made it this far, it's not inside any opaque brushes */\r
2511         return cluster;\r
2512 }\r
2513 \r
2514 \r
2515 \r
2516 /*\r
2517 ClusterForPointExtFilter() - ydnar\r
2518 adds cluster checking against a list of known valid clusters\r
2519 */\r
2520 \r
2521 int ClusterForPointExtFilter( vec3_t point, float epsilon, int numClusters, int *clusters )\r
2522 {\r
2523         int             i, cluster;\r
2524         \r
2525         \r
2526         /* get cluster for point */\r
2527         cluster = ClusterForPointExt( point, epsilon );\r
2528         \r
2529         /* check if filtering is necessary */\r
2530         if( cluster < 0 || numClusters <= 0 || clusters == NULL )\r
2531                 return cluster;\r
2532         \r
2533         /* filter */\r
2534         for( i = 0; i < numClusters; i++ )\r
2535         {\r
2536                 if( cluster == clusters[ i ] || ClusterVisible( cluster, clusters[ i ] ) )\r
2537                         return cluster;\r
2538         }\r
2539         \r
2540         /* failed */\r
2541         return -1;\r
2542 }\r
2543 \r
2544 \r
2545 \r
2546 /*\r
2547 ShaderForPointInLeaf() - ydnar\r
2548 checks a point against all brushes in a leaf, returning the shader of the brush\r
2549 also sets the cumulative surface and content flags for the brush hit\r
2550 */\r
2551 \r
2552 int ShaderForPointInLeaf( vec3_t point, int leafNum, float epsilon, int wantContentFlags, int wantSurfaceFlags, int *contentFlags, int *surfaceFlags )\r
2553 {\r
2554         int                             i, j;\r
2555         float                   dot;\r
2556         qboolean                inside;\r
2557         int                             *brushes, numBSPBrushes;\r
2558         bspLeaf_t                       *leaf;\r
2559         bspBrush_t              *brush;\r
2560         bspBrushSide_t  *side;\r
2561         bspPlane_t              *plane;\r
2562         bspShader_t             *shader;\r
2563         int                             allSurfaceFlags, allContentFlags;\r
2564 \r
2565         \r
2566         /* clear things out first */\r
2567         *surfaceFlags = 0;\r
2568         *contentFlags = 0;\r
2569         \r
2570         /* get leaf */\r
2571         if( leafNum < 0 )\r
2572                 return -1;\r
2573         leaf = &bspLeafs[ leafNum ];\r
2574         \r
2575         /* transparent leaf, so check point against all brushes in the leaf */\r
2576         brushes = &bspLeafBrushes[ leaf->firstBSPLeafBrush ];\r
2577         numBSPBrushes = leaf->numBSPLeafBrushes;\r
2578         for( i = 0; i < numBSPBrushes; i++ )\r
2579         {\r
2580                 /* get parts */\r
2581                 brush = &bspBrushes[ brushes[ i ] ];\r
2582                 \r
2583                 /* check point against all planes */\r
2584                 inside = qtrue;\r
2585                 allSurfaceFlags = 0;\r
2586                 allContentFlags = 0;\r
2587                 for( j = 0; j < brush->numSides && inside; j++ )\r
2588                 {\r
2589                         side = &bspBrushSides[ brush->firstSide + j ];\r
2590                         plane = &bspPlanes[ side->planeNum ];\r
2591                         dot = DotProduct( point, plane->normal );\r
2592                         dot -= plane->dist;\r
2593                         if( dot > epsilon )\r
2594                                 inside = qfalse;\r
2595                         else\r
2596                         {\r
2597                                 shader = &bspShaders[ side->shaderNum ];\r
2598                                 allSurfaceFlags |= shader->surfaceFlags;\r
2599                                 allContentFlags |= shader->contentFlags;\r
2600                         }\r
2601                 }\r
2602                 \r
2603                 /* handle if inside */\r
2604                 if( inside )\r
2605                 {\r
2606                         /* if there are desired flags, check for same and continue if they aren't matched */\r
2607                         if( wantContentFlags && !(wantContentFlags & allContentFlags) )\r
2608                                 continue;\r
2609                         if( wantSurfaceFlags && !(wantSurfaceFlags & allSurfaceFlags) )\r
2610                                 continue;\r
2611                         \r
2612                         /* store the cumulative flags and return the brush shader (which is mostly useless) */\r
2613                         *surfaceFlags = allSurfaceFlags;\r
2614                         *contentFlags = allContentFlags;\r
2615                         return brush->shaderNum;\r
2616                 }\r
2617         }\r
2618         \r
2619         /* if the point made it this far, it's not inside any brushes */\r
2620         return -1;\r
2621 }\r
2622 \r
2623 \r
2624 \r
2625 /*\r
2626 ChopBounds()\r
2627 chops a bounding box by the plane defined by origin and normal\r
2628 returns qfalse if the bounds is entirely clipped away\r
2629 \r
2630 this is not exactly the fastest way to do this...\r
2631 */\r
2632 \r
2633 qboolean ChopBounds( vec3_t mins, vec3_t maxs, vec3_t origin, vec3_t normal )\r
2634 {\r
2635         /* FIXME: rewrite this so it doesn't use bloody brushes */\r
2636         return qtrue;\r
2637 }\r
2638 \r
2639 \r
2640 \r
2641 /*\r
2642 SetupEnvelopes()\r
2643 calculates each light's effective envelope,\r
2644 taking into account brightness, type, and pvs.\r
2645 */\r
2646 \r
2647 #define LIGHT_EPSILON   0.125f\r
2648 #define LIGHT_NUDGE             2.0f\r
2649 \r
2650 void SetupEnvelopes( qboolean forGrid, qboolean fastFlag )\r
2651 {\r
2652         int                     i, x, y, z, x1, y1, z1;\r
2653         light_t         *light, *light2, **owner;\r
2654         bspLeaf_t       *leaf;\r
2655         vec3_t          origin, dir, mins, maxs, nullVector = { 0, 0, 0 };\r
2656         float           radius, intensity;\r
2657         light_t         *buckets[ 256 ];\r
2658         \r
2659         \r
2660         /* early out for weird cases where there are no lights */\r
2661         if( lights == NULL )\r
2662                 return;\r
2663         \r
2664         /* note it */\r
2665         Sys_FPrintf( SYS_VRB, "--- SetupEnvelopes%s ---\n", fastFlag ? " (fast)" : "" );\r
2666         \r
2667         /* count lights */\r
2668         numLights = 0;\r
2669         numCulledLights = 0;\r
2670         owner = &lights;\r
2671         while( *owner != NULL )\r
2672         {\r
2673                 /* get light */\r
2674                 light = *owner;\r
2675                 \r
2676                 /* handle negative lights */\r
2677                 if( light->photons < 0.0f || light->add < 0.0f )\r
2678                 {\r
2679                         light->photons *= -1.0f;\r
2680                         light->add *= -1.0f;\r
2681                         light->flags |= LIGHT_NEGATIVE;\r
2682                 }\r
2683                 \r
2684                 /* sunlight? */\r
2685                 if( light->type == EMIT_SUN )\r
2686                 {\r
2687                         /* special cased */\r
2688                         light->cluster = 0;\r
2689                         light->envelope = MAX_WORLD_COORD * 8.0f;\r
2690                         VectorSet( light->mins, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f, MIN_WORLD_COORD * 8.0f );\r
2691                         VectorSet( light->maxs, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f, MAX_WORLD_COORD * 8.0f );\r
2692                 }\r
2693                 \r
2694                 /* everything else */\r
2695                 else\r
2696                 {\r
2697                         /* get pvs cluster for light */\r
2698                         light->cluster = ClusterForPointExt( light->origin, LIGHT_EPSILON );\r
2699                         \r
2700                         /* invalid cluster? */\r
2701                         if( light->cluster < 0 )\r
2702                         {\r
2703                                 /* nudge the sample point around a bit */\r
2704                                 for( x = 0; x < 4; x++ )\r
2705                                 {\r
2706                                         /* two's complement 0, 1, -1, 2, -2, etc */\r
2707                                         x1 = ((x >> 1) ^ (x & 1 ? -1 : 0)) + (x & 1);\r
2708                                         \r
2709                                         for( y = 0; y < 4; y++ )\r
2710                                         {\r
2711                                                 y1 = ((y >> 1) ^ (y & 1 ? -1 : 0)) + (y & 1);\r
2712                                                 \r
2713                                                 for( z = 0; z < 4; z++ )\r
2714                                                 {\r
2715                                                         z1 = ((z >> 1) ^ (z & 1 ? -1 : 0)) + (z & 1);\r
2716                                                         \r
2717                                                         /* nudge origin */\r
2718                                                         origin[ 0 ] = light->origin[ 0 ] + (LIGHT_NUDGE * x1);\r
2719                                                         origin[ 1 ] = light->origin[ 1 ] + (LIGHT_NUDGE * y1);\r
2720                                                         origin[ 2 ] = light->origin[ 2 ] + (LIGHT_NUDGE * z1);\r
2721                                                         \r
2722                                                         /* try at nudged origin */\r
2723                                                         light->cluster = ClusterForPointExt( origin, LIGHT_EPSILON );\r
2724                                                         if( light->cluster < 0 )\r
2725                                                                 continue;\r
2726                                                                         \r
2727                                                         /* set origin */\r
2728                                                         VectorCopy( origin, light->origin );\r
2729                                                 }\r
2730                                         }\r
2731                                 }\r
2732                         }\r
2733                         \r
2734                         /* only calculate for lights in pvs and outside of opaque brushes */\r
2735                         if( light->cluster >= 0 )\r
2736                         {\r
2737                                 /* set light fast flag */\r
2738                                 if( fastFlag )\r
2739                                         light->flags |= LIGHT_FAST_TEMP;\r
2740                                 else\r
2741                                         light->flags &= ~LIGHT_FAST_TEMP;\r
2742                                 if( light->si && light->si->noFast )\r
2743                                         light->flags &= ~(LIGHT_FAST | LIGHT_FAST_TEMP);\r
2744                                 \r
2745                                 /* clear light envelope */\r
2746                                 light->envelope = 0;\r
2747                                 \r
2748                                 /* handle area lights */\r
2749                                 if( exactPointToPolygon && light->type == EMIT_AREA && light->w != NULL )\r
2750                                 {\r
2751                                         /* ugly hack to calculate extent for area lights, but only done once */\r
2752                                         VectorScale( light->normal, -1.0f, dir );\r
2753                                         for( radius = 100.0f; radius < 130000.0f && light->envelope == 0; radius += 10.0f )\r
2754                                         {\r
2755                                                 float   factor;\r
2756                                                 \r
2757                                                 VectorMA( light->origin, radius, light->normal, origin );\r
2758                                                 factor = PointToPolygonFormFactor( origin, dir, light->w );\r
2759                                                 if( factor < 0.0f )\r
2760                                                         factor *= -1.0f;\r
2761                                                 if( (factor * light->add) <= light->falloffTolerance )\r
2762                                                         light->envelope = radius;\r
2763                                         }\r
2764                                         \r
2765                                         /* check for fast mode */\r
2766                                         if( !(light->flags & LIGHT_FAST) && !(light->flags & LIGHT_FAST_TEMP) )\r
2767                                                 light->envelope = MAX_WORLD_COORD * 8.0f;\r
2768                                 }\r
2769                                 else\r
2770                                 {\r
2771                                         radius = 0.0f;\r
2772                                         intensity = light->photons;\r
2773                                 }\r
2774                                 \r
2775                                 /* other calcs */\r
2776                                 if( light->envelope <= 0.0f )\r
2777                                 {\r
2778                                         /* solve distance for non-distance lights */\r
2779                                         if( !(light->flags & LIGHT_ATTEN_DISTANCE) )\r
2780                                                 light->envelope = MAX_WORLD_COORD * 8.0f;\r
2781                                         \r
2782                                         /* solve distance for linear lights */\r
2783                                         else if( (light->flags & LIGHT_ATTEN_LINEAR ) )\r
2784                                                 //% light->envelope = ((intensity / light->falloffTolerance) * linearScale - 1 + radius) / light->fade;\r
2785                                                 light->envelope = ((intensity * linearScale) - light->falloffTolerance) / light->fade;\r
2786 \r
2787                                                 /*\r
2788                                                 add = angle * light->photons * linearScale - (dist * light->fade);\r
2789                                                 T = (light->photons * linearScale) - (dist * light->fade);\r
2790                                                 T + (dist * light->fade) = (light->photons * linearScale);\r
2791                                                 dist * light->fade = (light->photons * linearScale) - T;\r
2792                                                 dist = ((light->photons * linearScale) - T) / light->fade;\r
2793                                                 */\r
2794                                         \r
2795                                         /* solve for inverse square falloff */\r
2796                                         else\r
2797                                                 light->envelope = sqrt( intensity / light->falloffTolerance ) + radius;\r
2798                                                 \r
2799                                                 /*\r
2800                                                 add = light->photons / (dist * dist);\r
2801                                                 T = light->photons / (dist * dist);\r
2802                                                 T * (dist * dist) = light->photons;\r
2803                                                 dist = sqrt( light->photons / T );\r
2804                                                 */\r
2805                                 }\r
2806                                 \r
2807                                 /* chop radius against pvs */\r
2808                                 {\r
2809                                         /* clear bounds */\r
2810                                         ClearBounds( mins, maxs );\r
2811                                         \r
2812                                         /* check all leaves */\r
2813                                         for( i = 0; i < numBSPLeafs; i++ )\r
2814                                         {\r
2815                                                 /* get test leaf */\r
2816                                                 leaf = &bspLeafs[ i ];\r
2817                                                 \r
2818                                                 /* in pvs? */\r
2819                                                 if( leaf->cluster < 0 )\r
2820                                                         continue;\r
2821                                                 if( ClusterVisible( light->cluster, leaf->cluster ) == qfalse ) /* ydnar: thanks Arnout for exposing my stupid error (this never failed before) */\r
2822                                                         continue;\r
2823                                                 \r
2824                                                 /* add this leafs bbox to the bounds */\r
2825                                                 VectorCopy( leaf->mins, origin );\r
2826                                                 AddPointToBounds( origin, mins, maxs );\r
2827                                                 VectorCopy( leaf->maxs, origin );\r
2828                                                 AddPointToBounds( origin, mins, maxs );\r
2829                                         }\r
2830                                         \r
2831                                         /* test to see if bounds encompass light */\r
2832                                         for( i = 0; i < 3; i++ )\r
2833                                         {\r
2834                                                 if( mins[ i ] > light->origin[ i ] || maxs[ i ] < light->origin[ i ] )\r
2835                                                 {\r
2836                                                         //% Sys_Printf( "WARNING: Light PVS bounds (%.0f, %.0f, %.0f) -> (%.0f, %.0f, %.0f)\ndo not encompass light %d (%f, %f, %f)\n",\r
2837                                                         //%     mins[ 0 ], mins[ 1 ], mins[ 2 ],\r
2838                                                         //%     maxs[ 0 ], maxs[ 1 ], maxs[ 2 ],\r
2839                                                         //%     numLights, light->origin[ 0 ], light->origin[ 1 ], light->origin[ 2 ] );\r
2840                                                         AddPointToBounds( light->origin, mins, maxs );\r
2841                                                 }\r
2842                                         }\r
2843                                         \r
2844                                         /* chop the bounds by a plane for area lights and spotlights */\r
2845                                         if( light->type == EMIT_AREA || light->type == EMIT_SPOT )\r
2846                                                 ChopBounds( mins, maxs, light->origin, light->normal );\r
2847                                         \r
2848                                         /* copy bounds */\r
2849                                         VectorCopy( mins, light->mins );\r
2850                                         VectorCopy( maxs, light->maxs );\r
2851                                         \r
2852                                         /* reflect bounds around light origin */\r
2853                                         //%     VectorMA( light->origin, -1.0f, origin, origin );\r
2854                                         VectorScale( light->origin, 2, origin );\r
2855                                         VectorSubtract( origin, maxs, origin );\r
2856                                         AddPointToBounds( origin, mins, maxs );\r
2857                                         //%     VectorMA( light->origin, -1.0f, mins, origin );\r
2858                                         VectorScale( light->origin, 2, origin );\r
2859                                         VectorSubtract( origin, mins, origin );\r
2860                                         AddPointToBounds( origin, mins, maxs );\r
2861                                          \r
2862                                         /* calculate spherical bounds */\r
2863                                         VectorSubtract( maxs, light->origin, dir );\r
2864                                         radius = (float) VectorLength( dir );\r
2865                                         \r
2866                                         /* if this radius is smaller than the envelope, then set the envelope to it */\r
2867                                         if( radius < light->envelope )\r
2868                                         {\r
2869                                                 light->envelope = radius;\r
2870                                                 //%     Sys_FPrintf( SYS_VRB, "PVS Cull (%d): culled\n", numLights );\r
2871                                         }\r
2872                                         //%     else\r
2873                                         //%             Sys_FPrintf( SYS_VRB, "PVS Cull (%d): failed (%8.0f > %8.0f)\n", numLights, radius, light->envelope );\r
2874                                 }\r
2875                                 \r
2876                                 /* add grid/surface only check */\r
2877                                 if( forGrid )\r
2878                                 {\r
2879                                         if( !(light->flags & LIGHT_GRID) )\r
2880                                                 light->envelope = 0.0f;\r
2881                                 }\r
2882                                 else\r
2883                                 {\r
2884                                         if( !(light->flags & LIGHT_SURFACES) )\r
2885                                                 light->envelope = 0.0f;\r
2886                                 }\r
2887                         }\r
2888                         \r
2889                         /* culled? */\r
2890                         if( light->cluster < 0 || light->envelope <= 0.0f )\r
2891                         {\r
2892                                 /* debug code */\r
2893                                 //%     Sys_Printf( "Culling light: Cluster: %d Envelope: %f\n", light->cluster, light->envelope );\r
2894                                 \r
2895                                 /* delete the light */\r
2896                                 numCulledLights++;\r
2897                                 *owner = light->next;\r
2898                                 if( light->w != NULL )\r
2899                                         free( light->w );\r
2900                                 free( light );\r
2901                                 continue;\r
2902                         }\r
2903                 }\r
2904                 \r
2905                 /* square envelope */\r
2906                 light->envelope2 = (light->envelope * light->envelope);\r
2907                 \r
2908                 /* increment light count */\r
2909                 numLights++;\r
2910                 \r
2911                 /* set next light */\r
2912                 owner = &((**owner).next);\r
2913         }\r
2914         \r
2915         /* bucket sort lights by style */\r
2916         memset( buckets, 0, sizeof( buckets ) );\r
2917         light2 = NULL;\r
2918         for( light = lights; light != NULL; light = light2 )\r
2919         {\r
2920                 /* get next light */\r
2921                 light2 = light->next;\r
2922                 \r
2923                 /* filter into correct bucket */\r
2924                 light->next = buckets[ light->style ];\r
2925                 buckets[ light->style ] = light;\r
2926         }\r
2927         \r
2928         /* filter back into light list */\r
2929         lights = NULL;\r
2930         for( i = 255; i >= 0; i-- )\r
2931         {\r
2932                 light2 = NULL;\r
2933                 for( light = buckets[ i ]; light != NULL; light = light2 )\r
2934                 {\r
2935                         light2 = light->next;\r
2936                         light->next = lights;\r
2937                         lights = light;\r
2938                 }\r
2939         }\r
2940         \r
2941         /* emit some statistics */\r
2942         Sys_Printf( "%9d total lights\n", numLights );\r
2943         Sys_Printf( "%9d culled lights\n", numCulledLights );\r
2944 }\r
2945 \r
2946 \r
2947 \r
2948 /*\r
2949 CreateTraceLightsForBounds()\r
2950 creates a list of lights that affect the given bounding box and pvs clusters (bsp leaves)\r
2951 */\r
2952 \r
2953 void CreateTraceLightsForBounds( vec3_t mins, vec3_t maxs, vec3_t normal, int numClusters, int *clusters, int flags, trace_t *trace )\r
2954 {\r
2955         int                     i;\r
2956         light_t         *light;\r
2957         vec3_t          origin, dir, nullVector = { 0.0f, 0.0f, 0.0f };\r
2958         float           radius, dist, length;\r
2959         \r
2960         \r
2961         /* potential pre-setup  */\r
2962         if( numLights == 0 )\r
2963                 SetupEnvelopes( qfalse, fast );\r
2964         \r
2965         /* debug code */\r
2966         //% Sys_Printf( "CTWLFB: (%4.1f %4.1f %4.1f) (%4.1f %4.1f %4.1f)\n", mins[ 0 ], mins[ 1 ], mins[ 2 ], maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] );\r
2967         \r
2968         /* allocate the light list */\r
2969         trace->lights = safe_malloc( sizeof( light_t* ) * (numLights + 1) );\r
2970         trace->numLights = 0;\r
2971         \r
2972         /* calculate spherical bounds */\r
2973         VectorAdd( mins, maxs, origin );\r
2974         VectorScale( origin, 0.5f, origin );\r
2975         VectorSubtract( maxs, origin, dir );\r
2976         radius = (float) VectorLength( dir );\r
2977         \r
2978         /* get length of normal vector */\r
2979         if( normal != NULL )\r
2980                 length = VectorLength( normal );\r
2981         else\r
2982         {\r
2983                 normal = nullVector;\r
2984                 length = 0;\r
2985         }\r
2986         \r
2987         /* test each light and see if it reaches the sphere */\r
2988         /* note: the attenuation code MUST match LightingAtSample() */\r
2989         for( light = lights; light; light = light->next )\r
2990         {\r
2991                 /* check zero sized envelope */\r
2992                 if( light->envelope <= 0 )\r
2993                 {\r
2994                         lightsEnvelopeCulled++;\r
2995                         continue;\r
2996                 }\r
2997                 \r
2998                 /* check flags */\r
2999                 if( !(light->flags & flags) )\r
3000                         continue;\r
3001                 \r
3002                 /* sunlight skips all this nonsense */\r
3003                 if( light->type != EMIT_SUN )\r
3004                 {\r
3005                         /* sun only? */\r
3006                         if( sunOnly )\r
3007                                 continue;\r
3008                         \r
3009                         /* check against pvs cluster */\r
3010                         if( numClusters > 0 && clusters != NULL )\r
3011                         {\r
3012                                 for( i = 0; i < numClusters; i++ )\r
3013                                 {\r
3014                                         if( ClusterVisible( light->cluster, clusters[ i ] ) )\r
3015                                                 break;\r
3016                                 }\r
3017                                 \r
3018                                 /* fixme! */\r
3019                                 if( i == numClusters )\r
3020                                 {\r
3021                                         lightsClusterCulled++;\r
3022                                         continue;\r
3023                                 }\r
3024                         }\r
3025                         \r
3026                         /* if the light's bounding sphere intersects with the bounding sphere then this light needs to be tested */\r
3027                         VectorSubtract( light->origin, origin, dir );\r
3028                         dist = VectorLength( dir );\r
3029                         dist -= light->envelope;\r
3030                         dist -= radius;\r
3031                         if( dist > 0 )\r
3032                         {\r
3033                                 lightsEnvelopeCulled++;\r
3034                                 continue;\r
3035                         }\r
3036                         \r
3037                         /* check bounding box against light's pvs envelope (note: this code never eliminated any lights, so disabling it) */\r
3038                         #if 0\r
3039                         skip = qfalse;\r
3040                         for( i = 0; i < 3; i++ )\r
3041                         {\r
3042                                 if( mins[ i ] > light->maxs[ i ] || maxs[ i ] < light->mins[ i ] )\r
3043                                         skip = qtrue;\r
3044                         }\r
3045                         if( skip )\r
3046                         {\r
3047                                 lightsBoundsCulled++;\r
3048                                 continue;\r
3049                         }\r
3050                         #endif\r
3051                 }\r
3052                 \r
3053                 /* planar surfaces (except twosided surfaces) have a couple more checks */\r
3054                 if( length > 0.0f && trace->twoSided == qfalse )\r
3055                 {\r
3056                         /* lights coplanar with a surface won't light it */\r
3057                         if( !(light->flags & LIGHT_TWOSIDED) && DotProduct( light->normal, normal ) > 0.999f )\r
3058                         {\r
3059                                 lightsPlaneCulled++;\r
3060                                 continue;\r
3061                         }\r
3062                         \r
3063                         /* check to see if light is behind the plane */\r
3064                         if( DotProduct( light->origin, normal ) - DotProduct( origin, normal ) < -1.0f )\r
3065                         {\r
3066                                 lightsPlaneCulled++;\r
3067                                 continue;\r
3068                         }\r
3069                 }\r
3070                 \r
3071                 /* add this light */\r
3072                 trace->lights[ trace->numLights++ ] = light;\r
3073         }\r
3074         \r
3075         /* make last night null */\r
3076         trace->lights[ trace->numLights ] = NULL;\r
3077 }\r
3078 \r
3079 \r
3080 \r
3081 void FreeTraceLights( trace_t *trace )\r
3082 {\r
3083         if( trace->lights != NULL )\r
3084                 free( trace->lights );\r
3085 }\r
3086 \r
3087 \r
3088 \r
3089 /*\r
3090 CreateTraceLightsForSurface()\r
3091 creates a list of lights that can potentially affect a drawsurface\r
3092 */\r
3093 \r
3094 void CreateTraceLightsForSurface( int num, trace_t *trace )\r
3095 {\r
3096         int                                     i;\r
3097         vec3_t                          mins, maxs, normal;\r
3098         bspDrawVert_t           *dv;\r
3099         bspDrawSurface_t        *ds;\r
3100         surfaceInfo_t           *info;\r
3101         \r
3102         \r
3103         /* dummy check */\r
3104         if( num < 0 )\r
3105                 return;\r
3106         \r
3107         /* get drawsurface and info */\r
3108         ds = &bspDrawSurfaces[ num ];\r
3109         info = &surfaceInfos[ num ];\r
3110         \r
3111         /* get the mins/maxs for the dsurf */\r
3112         ClearBounds( mins, maxs );\r
3113         VectorCopy( bspDrawVerts[ ds->firstVert ].normal, normal );\r
3114         for( i = 0; i < ds->numVerts; i++ )\r
3115         {\r
3116                 dv = &yDrawVerts[ ds->firstVert + i ];\r
3117                 AddPointToBounds( dv->xyz, mins, maxs );\r
3118                 if( !VectorCompare( dv->normal, normal ) )\r
3119                         VectorClear( normal );\r
3120         }\r
3121         \r
3122         /* create the lights for the bounding box */\r
3123         CreateTraceLightsForBounds( mins, maxs, normal, info->numSurfaceClusters, &surfaceClusters[ info->firstSurfaceCluster ], LIGHT_SURFACES, trace );\r
3124 }\r
3125 \r
3126 \r
3127 \r
3128 \r
3129 \r