]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/light_bounce.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3map2 / light_bounce.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_BOUNCE_C\r
32 \r
33 \r
34 \r
35 /* dependencies */\r
36 #include "q3map2.h"\r
37 \r
38 \r
39 \r
40 /* functions */\r
41 \r
42 /*\r
43 RadFreeLights()\r
44 deletes any existing lights, freeing up memory for the next bounce\r
45 */\r
46 \r
47 void RadFreeLights( void )\r
48 {\r
49         light_t         *light, *next;\r
50         \r
51         \r
52         /* delete lights */\r
53         for( light = lights; light; light = next )\r
54         {\r
55                 next = light->next;\r
56                 if( light->w != NULL )\r
57                         FreeWinding( light->w );\r
58                 free( light );\r
59         }\r
60         numLights = 0;\r
61         lights = NULL;\r
62 }\r
63 \r
64 \r
65 \r
66 /*\r
67 RadClipWindingEpsilon()\r
68 clips a rad winding by a plane\r
69 based off the regular clip winding code\r
70 */\r
71 \r
72 static void RadClipWindingEpsilon( radWinding_t *in, vec3_t normal, vec_t dist,\r
73         vec_t epsilon, radWinding_t *front, radWinding_t *back, clipWork_t *cw )\r
74 {\r
75         vec_t                   *dists;\r
76         int                             *sides;\r
77         int                             counts[ 3 ];\r
78         vec_t                   dot;            /* ydnar: changed from static b/c of threading */ /* VC 4.2 optimizer bug if not static? */\r
79         int                             i, j, k;\r
80         radVert_t               *v1, *v2, mid;\r
81         int                             maxPoints;\r
82         \r
83         \r
84         /* crutch */\r
85         dists = cw->dists;\r
86         sides = cw->sides;\r
87         \r
88         /* clear counts */\r
89         counts[ 0 ] = counts[ 1 ] = counts[ 2 ] = 0;\r
90 \r
91         /* determine sides for each point */\r
92         for( i = 0; i < in->numVerts; i++ )\r
93         {\r
94                 dot = DotProduct( in->verts[ i ].xyz, normal );\r
95                 dot -= dist;\r
96                 dists[ i ] = dot;\r
97                 if( dot > epsilon )\r
98                         sides[ i ] = SIDE_FRONT;\r
99                 else if( dot < -epsilon )\r
100                         sides[ i ] = SIDE_BACK;\r
101                 else\r
102                         sides[ i ] = SIDE_ON;\r
103                 counts[ sides[ i ] ]++;\r
104         }\r
105         sides[ i ] = sides[ 0 ];\r
106         dists[ i ] = dists[ 0 ];\r
107         \r
108         /* clear front and back */\r
109         front->numVerts = back->numVerts = 0;\r
110         \r
111         /* handle all on one side cases */\r
112         if( counts[ 0 ] == 0 )\r
113         {\r
114                 memcpy( back, in, sizeof( radWinding_t ) );\r
115                 return;\r
116         }\r
117         if( counts[ 1 ] == 0 )\r
118         {\r
119                 memcpy( front, in, sizeof( radWinding_t ) );\r
120                 return;\r
121         }\r
122         \r
123         /* setup windings */\r
124         maxPoints = in->numVerts + 4;\r
125         \r
126         /* do individual verts */\r
127         for( i = 0; i < in->numVerts; i++ )\r
128         {\r
129                 /* do simple vertex copies first */\r
130                 v1 = &in->verts[ i ];\r
131                 \r
132                 if( sides[ i ] == SIDE_ON )\r
133                 {\r
134                         memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );\r
135                         memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );\r
136                         continue;\r
137                 }\r
138         \r
139                 if( sides[ i ] == SIDE_FRONT )\r
140                         memcpy( &front->verts[ front->numVerts++ ], v1, sizeof( radVert_t ) );\r
141                 \r
142                 if( sides[ i ] == SIDE_BACK )\r
143                         memcpy( &back->verts[ back->numVerts++ ], v1, sizeof( radVert_t ) );\r
144                 \r
145                 if( sides[ i + 1 ] == SIDE_ON || sides[ i + 1 ] == sides[ i ] )\r
146                         continue;\r
147                         \r
148                 /* generate a split vertex */\r
149                 v2 = &in->verts[ (i + 1) % in->numVerts ];\r
150                 \r
151                 dot = dists[ i ] / (dists[ i ] - dists[ i + 1 ]);\r
152 \r
153                 /* average vertex values */\r
154                 for( j = 0; j < 4; j++ )\r
155                 {\r
156                         /* color */\r
157                         if( j < 4 )\r
158                         {\r
159                                 for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
160                                         mid.color[ k ][ j ] = v1->color[ k ][ j ] + dot * (v2->color[ k ][ j ] - v1->color[ k ][ j ]);\r
161                         }\r
162                         \r
163                         /* xyz, normal */\r
164                         if( j < 3 )\r
165                         {\r
166                                 mid.xyz[ j ] = v1->xyz[ j ] + dot * (v2->xyz[ j ] - v1->xyz[ j ]);\r
167                                 mid.normal[ j ] = v1->normal[ j ] + dot * (v2->normal[ j ] - v1->normal[ j ]);\r
168                         }\r
169                         \r
170                         /* st, lightmap */\r
171                         if( j < 2 )\r
172                         {\r
173                                 mid.st[ j ] = v1->st[ j ] + dot * (v2->st[ j ] - v1->st[ j ]);\r
174                                 for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
175                                         mid.lightmap[ k ][ j ] = v1->lightmap[ k ][ j ] + dot * (v2->lightmap[ k ][ j ] - v1->lightmap[ k ][ j ]);\r
176                         }\r
177                 }\r
178                 \r
179                 /* normalize the averaged normal */\r
180                 VectorNormalize( mid.normal, mid.normal );\r
181 \r
182                 /* copy the midpoint to both windings */\r
183                 memcpy( &front->verts[ front->numVerts++ ], &mid, sizeof( radVert_t ) );\r
184                 memcpy( &back->verts[ back->numVerts++ ], &mid, sizeof( radVert_t ) );\r
185         }\r
186         \r
187         /* error check */\r
188         if( front->numVerts > maxPoints || front->numVerts > maxPoints )\r
189                 Error( "RadClipWindingEpsilon: points exceeded estimate" );\r
190         if( front->numVerts > MAX_POINTS_ON_WINDING || front->numVerts > MAX_POINTS_ON_WINDING )\r
191                 Error( "RadClipWindingEpsilon: MAX_POINTS_ON_WINDING" );\r
192 }\r
193 \r
194 \r
195 \r
196 \r
197 \r
198 /*\r
199 RadSampleImage()\r
200 samples a texture image for a given color\r
201 returns qfalse if pixels are bad\r
202 */\r
203 \r
204 qboolean RadSampleImage( byte *pixels, int width, int height, float st[ 2 ], float color[ 4 ] )\r
205 {\r
206         float   sto[ 2 ];\r
207         int             x, y;\r
208         \r
209         \r
210         /* clear color first */\r
211         color[ 0 ] = color[ 1 ] = color[ 2 ] = color[ 3 ] = 255;\r
212         \r
213         /* dummy check */\r
214         if( pixels == NULL || width < 1 || height < 1 )\r
215                 return qfalse;\r
216         \r
217         /* bias st */\r
218         sto[ 0 ] = st[ 0 ];\r
219         while( sto[ 0 ] < 0.0f )\r
220                 sto[ 0 ] += 1.0f;\r
221         sto[ 1 ] = st[ 1 ];\r
222         while( sto[ 1 ] < 0.0f )\r
223                 sto[ 1 ] += 1.0f;\r
224 \r
225         /* get offsets */\r
226         x = ((float) width * sto[ 0 ]) + 0.5f;\r
227         x %= width;\r
228         y = ((float) height * sto[ 1 ])  + 0.5f;\r
229         y %= height;\r
230         \r
231         /* get pixel */\r
232         pixels += (y * width * 4) + (x * 4);\r
233         VectorCopy( pixels, color );\r
234         color[ 3 ] = pixels[ 3 ];\r
235         return qtrue;\r
236 }\r
237 \r
238 \r
239 \r
240 /*\r
241 RadSample()\r
242 samples a fragment's lightmap or vertex color and returns an\r
243 average color and a color gradient for the sample\r
244 */\r
245 \r
246 #define MAX_SAMPLES                     150\r
247 #define SAMPLE_GRANULARITY      6\r
248 \r
249 static void RadSample( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si, radWinding_t *rw, vec3_t average, vec3_t gradient, int *style )\r
250 {\r
251         int                     i, j, k, l, v, x, y, samples;\r
252         vec3_t          color, mins, maxs;\r
253         vec4_t          textureColor;\r
254         float           alpha, alphaI, bf;\r
255         vec3_t          blend;\r
256         float           st[ 2 ], lightmap[ 2 ], *radLuxel;\r
257         radVert_t       *rv[ 3 ];\r
258         \r
259         \r
260         /* initial setup */\r
261         ClearBounds( mins, maxs );\r
262         VectorClear( average );\r
263         VectorClear( gradient );\r
264         alpha = 0;\r
265         \r
266         /* dummy check */\r
267         if( rw == NULL || rw->numVerts < 3 )\r
268                 return;\r
269         \r
270         /* start sampling */\r
271         samples = 0;\r
272         \r
273         /* sample vertex colors if no lightmap or this is the initial pass */\r
274         if( lm == NULL || lm->radLuxels[ lightmapNum ] == NULL || bouncing == qfalse )\r
275         {\r
276                 for( samples = 0; samples < rw->numVerts; samples++ )\r
277                 {\r
278                         /* multiply by texture color */\r
279                         if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, rw->verts[ samples ].st, textureColor ) )\r
280                         {\r
281                                 VectorCopy( si->averageColor, textureColor );\r
282                                 textureColor[ 4 ] = 255.0f;\r
283                         }\r
284                         for( i = 0; i < 3; i++ )\r
285                                 color[ i ] = (textureColor[ i ] / 255) * (rw->verts[ samples ].color[ lightmapNum ][ i ] / 255.0f);\r
286                         \r
287                         AddPointToBounds( color, mins, maxs );\r
288                         VectorAdd( average, color, average );\r
289                         \r
290                         /* get alpha */\r
291                         alpha += (textureColor[ 3 ] / 255.0f) * (rw->verts[ samples ].color[ lightmapNum ][ 3 ] / 255.0f);\r
292                 }\r
293                 \r
294                 /* set style */\r
295                 *style = ds->vertexStyles[ lightmapNum ];\r
296         }\r
297         \r
298         /* sample lightmap */\r
299         else\r
300         {\r
301                 /* fracture the winding into a fan (including degenerate tris) */\r
302                 for( v = 1; v < (rw->numVerts - 1) && samples < MAX_SAMPLES; v++ )\r
303                 {\r
304                         /* get a triangle */\r
305                         rv[ 0 ] = &rw->verts[ 0 ];\r
306                         rv[ 1 ] = &rw->verts[ v ];\r
307                         rv[ 2 ] = &rw->verts[ v + 1 ];\r
308                         \r
309                         /* this code is embarassing (really should just rasterize the triangle) */\r
310                         for( i = 1; i < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; i++ )\r
311                         {\r
312                                 for( j = 1; j < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; j++ )\r
313                                 {\r
314                                         for( k = 1; k < SAMPLE_GRANULARITY && samples < MAX_SAMPLES; k++ )\r
315                                         {\r
316                                                 /* create a blend vector (barycentric coordinates) */\r
317                                                 blend[ 0 ] = i;\r
318                                                 blend[ 1 ] = j;\r
319                                                 blend[ 2 ] = k;\r
320                                                 bf = (1.0 / (blend[ 0 ] + blend[ 1 ] + blend[ 2 ]));\r
321                                                 VectorScale( blend, bf, blend );\r
322                                                 \r
323                                                 /* create a blended sample */\r
324                                                 st[ 0 ] = st[ 1 ] = 0.0f;\r
325                                                 lightmap[ 0 ] = lightmap[ 1 ] = 0.0f;\r
326                                                 alphaI = 0.0f;\r
327                                                 for( l = 0; l < 3; l++ )\r
328                                                 {\r
329                                                         st[ 0 ] += (rv[ l ]->st[ 0 ] * blend[ l ]);\r
330                                                         st[ 1 ] += (rv[ l ]->st[ 1 ] * blend[ l ]);\r
331                                                         lightmap[ 0 ] += (rv[ l ]->lightmap[ lightmapNum ][ 0 ] * blend[ l ]);\r
332                                                         lightmap[ 1 ] += (rv[ l ]->lightmap[ lightmapNum ][ 1 ] * blend[ l ]);\r
333                                                         alphaI += (rv[ l ]->color[ lightmapNum ][ 3 ] * blend[ l ]);\r
334                                                 }\r
335                                                 \r
336                                                 /* get lightmap xy coords */\r
337                                                 x = lightmap[ 0 ] / (float) superSample;\r
338                                                 y = lightmap[ 1 ] / (float) superSample;\r
339                                                 if( x < 0 )\r
340                                                         x = 0;\r
341                                                 else if ( x >= lm->w )\r
342                                                         x = lm->w - 1;\r
343                                                 if( y < 0 )\r
344                                                         y = 0;\r
345                                                 else if ( y >= lm->h )\r
346                                                         y = lm->h - 1;\r
347                                                 \r
348                                                 /* get radiosity luxel */\r
349                                                 radLuxel = RAD_LUXEL( lightmapNum, x, y );\r
350                                                 \r
351                                                 /* ignore unlit/unused luxels */\r
352                                                 if( radLuxel[ 0 ] < 0.0f )\r
353                                                         continue;\r
354                                                 \r
355                                                 /* inc samples */\r
356                                                 samples++;\r
357                                                 \r
358                                                 /* multiply by texture color */\r
359                                                 if( !RadSampleImage( si->lightImage->pixels, si->lightImage->width, si->lightImage->height, st, textureColor ) )\r
360                                                 {\r
361                                                         VectorCopy( si->averageColor, textureColor );\r
362                                                         textureColor[ 4 ] = 255;\r
363                                                 }\r
364                                                 for( i = 0; i < 3; i++ )\r
365                                                         color[ i ] = (textureColor[ i ] / 255) * (radLuxel[ i ] / 255);\r
366                                                 \r
367                                                 AddPointToBounds( color, mins, maxs );\r
368                                                 VectorAdd( average, color, average );\r
369                                                 \r
370                                                 /* get alpha */\r
371                                                 alpha += (textureColor[ 3 ] / 255) * (alphaI / 255);\r
372                                         }\r
373                                 }\r
374                         }\r
375                 }\r
376                 \r
377                 /* set style */\r
378                 *style = ds->lightmapStyles[ lightmapNum ];\r
379         }\r
380         \r
381         /* any samples? */\r
382         if( samples <= 0 )\r
383                 return;\r
384         \r
385         /* average the color */\r
386         VectorScale( average, (1.0 / samples), average );\r
387         \r
388         /* create the color gradient */\r
389         //%     VectorSubtract( maxs, mins, delta );\r
390         \r
391         /* new: color gradient will always be 0-1.0, expressed as the range of light relative to overall light */\r
392         //%     gradient[ 0 ] = maxs[ 0 ] > 0.0f ? (maxs[ 0 ] - mins[ 0 ]) / maxs[ 0 ] : 0.0f;\r
393         //%     gradient[ 1 ] = maxs[ 1 ] > 0.0f ? (maxs[ 1 ] - mins[ 1 ]) / maxs[ 1 ] : 0.0f;\r
394         //%     gradient[ 2 ] = maxs[ 2 ] > 0.0f ? (maxs[ 2 ] - mins[ 2 ]) / maxs[ 2 ] : 0.0f;\r
395         \r
396         /* newer: another contrast function */\r
397         for( i = 0; i < 3; i++ )\r
398                 gradient[ i ] = (maxs[ i ] - mins[ i ]) * maxs[ i ];\r
399 }\r
400 \r
401 \r
402 \r
403 /*\r
404 RadSubdivideDiffuseLight()\r
405 subdivides a radiosity winding until it is smaller than subdivide, then generates an area light\r
406 */\r
407 \r
408 #define RADIOSITY_MAX_GRADIENT          0.75f   //%     0.25f\r
409 #define RADIOSITY_VALUE                         500.0f\r
410 #define RADIOSITY_MIN                           0.0001f\r
411 #define RADIOSITY_CLIP_EPSILON          0.125f\r
412 \r
413 static void RadSubdivideDiffuseLight( int lightmapNum, bspDrawSurface_t *ds, rawLightmap_t *lm, shaderInfo_t *si,\r
414         float scale, float subdivide, qboolean original, radWinding_t *rw, clipWork_t *cw )\r
415 {\r
416         int                             i, style;\r
417         float                   dist, area, value;\r
418         vec3_t                  mins, maxs, normal, d1, d2, cross, color, gradient;\r
419         light_t                 *light, *splash;\r
420         winding_t               *w;\r
421         \r
422         \r
423         /* dummy check */\r
424         if( rw == NULL || rw->numVerts < 3 )\r
425                 return;\r
426         \r
427         /* get bounds for winding */\r
428         ClearBounds( mins, maxs );\r
429         for( i = 0; i < rw->numVerts; i++ )\r
430                 AddPointToBounds( rw->verts[ i ].xyz, mins, maxs );\r
431         \r
432         /* subdivide if necessary */\r
433         for( i = 0; i < 3; i++ )\r
434         {\r
435                 if( maxs[ i ] - mins[ i ] > subdivide )\r
436                 {\r
437                         radWinding_t    front, back;\r
438                         \r
439                         \r
440                         /* make axial plane */\r
441                         VectorClear( normal );\r
442                         normal[ i ] = 1;\r
443                         dist = (maxs[ i ] + mins[ i ]) * 0.5f;\r
444                         \r
445                         /* clip the winding */\r
446                         RadClipWindingEpsilon( rw, normal, dist, RADIOSITY_CLIP_EPSILON, &front, &back, cw );\r
447                         \r
448                         /* recurse */\r
449                         RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &front, cw );\r
450                         RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qfalse, &back, cw );\r
451                         return;\r
452                 }\r
453         }\r
454         \r
455         /* check area */\r
456         area = 0.0f;\r
457         for( i = 2; i < rw->numVerts; i++ )\r
458         {\r
459                 VectorSubtract( rw->verts[ i - 1 ].xyz, rw->verts[ 0 ].xyz, d1 );\r
460                 VectorSubtract( rw->verts[ i ].xyz, rw->verts[ 0 ].xyz, d2 );\r
461                 CrossProduct( d1, d2, cross );\r
462                 area += 0.5f * VectorLength( cross );\r
463         }\r
464         if( area < 1.0f || area > 20000000.0f )\r
465                 return;\r
466         \r
467         /* more subdivision may be necessary */\r
468         if( bouncing )\r
469         {\r
470                 /* get color sample for the surface fragment */\r
471                 RadSample( lightmapNum, ds, lm, si, rw, color, gradient, &style );\r
472                 \r
473                 /* if color gradient is too high, subdivide again */\r
474                 if( subdivide > minDiffuseSubdivide && \r
475                         (gradient[ 0 ] > RADIOSITY_MAX_GRADIENT || gradient[ 1 ] > RADIOSITY_MAX_GRADIENT || gradient[ 2 ] > RADIOSITY_MAX_GRADIENT) )\r
476                 {\r
477                         RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, (subdivide / 2.0f), qfalse, rw, cw );\r
478                         return;\r
479                 }\r
480         }\r
481         \r
482         /* create a regular winding and an average normal */\r
483         w = AllocWinding( rw->numVerts );\r
484         w->numpoints = rw->numVerts;\r
485         VectorClear( normal );\r
486         for( i = 0; i < rw->numVerts; i++ )\r
487         {\r
488                 VectorCopy( rw->verts[ i ].xyz, w->p[ i ] );\r
489                 VectorAdd( normal, rw->verts[ i ].normal, normal );\r
490         }\r
491         VectorScale( normal, (1.0f / rw->numVerts), normal );\r
492         if( VectorNormalize( normal, normal ) == 0.0f )\r
493                 return;\r
494         \r
495         /* early out? */\r
496         if( bouncing && VectorLength( color ) < RADIOSITY_MIN )\r
497                 return;\r
498         \r
499         /* debug code */\r
500         //%     Sys_Printf( "Size: %d %d %d\n", (int) (maxs[ 0 ] - mins[ 0 ]), (int) (maxs[ 1 ] - mins[ 1 ]), (int) (maxs[ 2 ] - mins[ 2 ]) );\r
501         //%     Sys_Printf( "Grad: %f %f %f\n", gradient[ 0 ], gradient[ 1 ], gradient[ 2 ] );\r
502         \r
503         /* increment counts */\r
504         numDiffuseLights++;\r
505         switch( ds->surfaceType )\r
506         {\r
507                 case MST_PLANAR:\r
508                         numBrushDiffuseLights++;\r
509                         break;\r
510                 \r
511                 case MST_TRIANGLE_SOUP:\r
512                         numTriangleDiffuseLights;\r
513                         break;\r
514                 \r
515                 case MST_PATCH:\r
516                         numPatchDiffuseLights++;\r
517                         break;\r
518         }\r
519         \r
520         /* create a light */\r
521         light = safe_malloc( sizeof( *light ) );\r
522         memset( light, 0, sizeof( *light ) );\r
523         \r
524         /* attach it */\r
525         ThreadLock();\r
526         light->next = lights;\r
527         lights = light;\r
528         ThreadUnlock();\r
529         \r
530         /* initialize the light */\r
531         light->flags = LIGHT_AREA_DEFAULT;\r
532         light->type = EMIT_AREA;\r
533         light->si = si;\r
534         light->fade = 1.0f;\r
535         light->w = w;\r
536         \r
537         /* set falloff threshold */\r
538         light->falloffTolerance = falloffTolerance;\r
539         \r
540         /* bouncing light? */\r
541         if( bouncing == qfalse )\r
542         {\r
543                 /* handle first-pass lights in normal q3a style */\r
544                 value = si->value;\r
545                 light->photons = value * area * areaScale;\r
546                 light->add = value * formFactorValueScale * areaScale;\r
547                 VectorCopy( si->color, light->color );\r
548                 VectorScale( light->color, light->add, light->emitColor );\r
549                 light->style = si->lightStyle;\r
550                 if( light->style < 0 || light->style >= LS_NONE )\r
551                         light->style = 0;\r
552                 \r
553                 /* set origin */\r
554                 VectorAdd( mins, maxs, light->origin );\r
555                 VectorScale( light->origin, 0.5f, light->origin );\r
556                 \r
557                 /* nudge it off the plane a bit */\r
558                 VectorCopy( normal, light->normal );\r
559                 VectorMA( light->origin, 1.0f, light->normal, light->origin );\r
560                 light->dist = DotProduct( light->origin, normal );\r
561                 \r
562                 /* optionally create a point splashsplash light for first pass */\r
563                 if( original && si->backsplashFraction > 0 )\r
564                 {\r
565                         /* allocate a new point light */\r
566                         splash = safe_malloc( sizeof( *splash ) );\r
567                         memset( splash, 0, sizeof( *splash ) );\r
568                         splash->next = lights;\r
569                         lights = splash;\r
570                         \r
571                         /* set it up */\r
572                         splash->flags = LIGHT_Q3A_DEFAULT;\r
573                         splash->type = EMIT_POINT;\r
574                         splash->photons = light->photons * si->backsplashFraction;\r
575                         splash->fade = 1.0f;\r
576                         splash->si = si;\r
577                         VectorMA( light->origin, si->backsplashDistance, normal, splash->origin );\r
578                         VectorCopy( si->color, splash->color );\r
579                         splash->falloffTolerance = falloffTolerance;\r
580                         splash->style = light->style;\r
581                         \r
582                         /* add to counts */\r
583                         numPointLights++;\r
584                 }\r
585         }\r
586         else\r
587         {\r
588                 /* handle bounced light (radiosity) a little differently */\r
589                 value = RADIOSITY_VALUE * si->bounceScale * 0.375f;\r
590                 light->photons = value * area * bounceScale;\r
591                 light->add = value * formFactorValueScale * bounceScale;\r
592                 VectorCopy( color, light->color );\r
593                 VectorScale( light->color, light->add, light->emitColor );\r
594                 light->style = style;\r
595                 if( light->style < 0 || light->style >= LS_NONE )\r
596                         light->style = 0;\r
597                 \r
598                 /* set origin */\r
599                 WindingCenter( w, light->origin );\r
600                 \r
601                 /* nudge it off the plane a bit */\r
602                 VectorCopy( normal, light->normal );\r
603                 VectorMA( light->origin, 1.0f, light->normal, light->origin );\r
604                 light->dist = DotProduct( light->origin, normal );\r
605         }\r
606         \r
607         /* emit light from both sides? */\r
608         if( si->compileFlags & C_FOG || si->twoSided )\r
609                 light->flags |= LIGHT_TWOSIDED;\r
610         \r
611         //%     Sys_Printf( "\nAL: C: (%6f, %6f, %6f) [%6f] N: (%6f, %6f, %6f) %s\n",\r
612         //%             light->color[ 0 ], light->color[ 1 ], light->color[ 2 ], light->add,\r
613         //%             light->normal[ 0 ], light->normal[ 1 ], light->normal[ 2 ],\r
614         //%             light->si->shader );\r
615 }\r
616 \r
617 \r
618 \r
619 /*\r
620 RadLightForTriangles()\r
621 creates unbounced diffuse lights for triangle soup (misc_models, etc)\r
622 */\r
623 \r
624 void RadLightForTriangles( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )\r
625 {\r
626         int                                     i, j, k, v;\r
627         bspDrawSurface_t        *ds;\r
628         surfaceInfo_t           *info;\r
629         float                           *radVertexLuxel;\r
630         radWinding_t            rw;\r
631         \r
632         \r
633         /* get surface */\r
634         ds = &bspDrawSurfaces[ num ];\r
635         info = &surfaceInfos[ num ];\r
636         \r
637         /* each triangle is a potential emitter */\r
638         rw.numVerts = 3;\r
639         for( i = 0; i < ds->numIndexes; i += 3 )\r
640         {\r
641                 /* copy each vert */\r
642                 for( j = 0; j < 3; j++ )\r
643                 {\r
644                         /* get vertex index and rad vertex luxel */\r
645                         v = ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ];\r
646                         \r
647                         /* get most everything */\r
648                         memcpy( &rw.verts[ j ], &yDrawVerts[ v ], sizeof( bspDrawVert_t ) );\r
649                         \r
650                         /* fix colors */\r
651                         for( k = 0; k < MAX_LIGHTMAPS; k++ )\r
652                         {\r
653                                 radVertexLuxel = RAD_VERTEX_LUXEL( k, ds->firstVert + bspDrawIndexes[ ds->firstIndex + i + j ] );\r
654                                 VectorCopy( radVertexLuxel, rw.verts[ j ].color[ k ] );\r
655                                 rw.verts[ j ].color[ k ][ 3 ] = yDrawVerts[ v ].color[ k ][ 3 ];\r
656                         }\r
657                 }\r
658                 \r
659                 /* subdivide into area lights */\r
660                 RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );\r
661         }\r
662 }\r
663 \r
664 \r
665 \r
666 /*\r
667 RadLightForPatch()\r
668 creates unbounced diffuse lights for patches\r
669 */\r
670 \r
671 #define PLANAR_EPSILON  0.1f\r
672 \r
673 void RadLightForPatch( int num, int lightmapNum, rawLightmap_t *lm, shaderInfo_t *si, float scale, float subdivide, clipWork_t *cw )\r
674 {\r
675         int                                     i, x, y, v, t, pw[ 5 ], r;\r
676         bspDrawSurface_t        *ds;\r
677         surfaceInfo_t           *info;\r
678         bspDrawVert_t           *bogus;\r
679         bspDrawVert_t           *dv[ 4 ];\r
680         mesh_t                          src, *subdivided, *mesh;\r
681         float                           *radVertexLuxel;\r
682         float                           dist;\r
683         vec4_t                          plane;\r
684         qboolean                        planar;\r
685         radWinding_t            rw;\r
686         \r
687         \r
688         /* get surface */\r
689         ds = &bspDrawSurfaces[ num ];\r
690         info = &surfaceInfos[ num ];\r
691         \r
692         /* construct a bogus vert list with color index stuffed into color[ 0 ] */\r
693         bogus = safe_malloc( ds->numVerts * sizeof( bspDrawVert_t ) );\r
694         memcpy( bogus, &yDrawVerts[ ds->firstVert ], ds->numVerts * sizeof( bspDrawVert_t ) );\r
695         for( i = 0; i < ds->numVerts; i++ )\r
696                 bogus[ i ].color[ 0 ][ 0 ] = i;\r
697         \r
698         /* build a subdivided mesh identical to shadow facets for this patch */\r
699         /* this MUST MATCH FacetsForPatch() identically! */\r
700         src.width = ds->patchWidth;\r
701         src.height = ds->patchHeight;\r
702         src.verts = bogus;\r
703         //%     subdivided = SubdivideMesh( src, 8, 512 );\r
704         subdivided = SubdivideMesh2( src, info->patchIterations );\r
705         PutMeshOnCurve( *subdivided );\r
706         //%     MakeMeshNormals( *subdivided );\r
707         mesh = RemoveLinearMeshColumnsRows( subdivided );\r
708         FreeMesh( subdivided );\r
709         free( bogus );\r
710         \r
711         /* FIXME: build interpolation table into color[ 1 ] */\r
712         \r
713         /* fix up color indexes */\r
714         for( i = 0; i < (mesh->width * mesh->height); i++ )\r
715         {\r
716                 dv[ 0 ] = &mesh->verts[ i ];\r
717                 if( dv[ 0 ]->color[ 0 ][ 0 ] >= ds->numVerts )\r
718                         dv[ 0 ]->color[ 0 ][ 0 ] = ds->numVerts - 1;\r
719         }\r
720         \r
721         /* iterate through the mesh quads */\r
722         for( y = 0; y < (mesh->height - 1); y++ )\r
723         {\r
724                 for( x = 0; x < (mesh->width - 1); x++ )\r
725                 {\r
726                         /* set indexes */\r
727                         pw[ 0 ] = x + (y * mesh->width);\r
728                         pw[ 1 ] = x + ((y + 1) * mesh->width);\r
729                         pw[ 2 ] = x + 1 + ((y + 1) * mesh->width);\r
730                         pw[ 3 ] = x + 1 + (y * mesh->width);\r
731                         pw[ 4 ] = x + (y * mesh->width);        /* same as pw[ 0 ] */\r
732                         \r
733                         /* set radix */\r
734                         r = (x + y) & 1;\r
735                         \r
736                         /* get drawverts */\r
737                         dv[ 0 ] = &mesh->verts[ pw[ r + 0 ] ];\r
738                         dv[ 1 ] = &mesh->verts[ pw[ r + 1 ] ];\r
739                         dv[ 2 ] = &mesh->verts[ pw[ r + 2 ] ];\r
740                         dv[ 3 ] = &mesh->verts[ pw[ r + 3 ] ];\r
741                         \r
742                         /* planar? */\r
743                         planar = PlaneFromPoints( plane, dv[ 0 ]->xyz, dv[ 1 ]->xyz, dv[ 2 ]->xyz );\r
744                         if( planar )\r
745                         {\r
746                                 dist = DotProduct( dv[ 1 ]->xyz, plane ) - plane[ 3 ];\r
747                                 if( fabs( dist ) > PLANAR_EPSILON )\r
748                                         planar = qfalse;\r
749                         }\r
750                         \r
751                         /* generate a quad */\r
752                         if( planar )\r
753                         {\r
754                                 rw.numVerts = 4;\r
755                                 for( v = 0; v < 4; v++ )\r
756                                 {\r
757                                         /* get most everything */\r
758                                         memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );\r
759                                         \r
760                                         /* fix colors */\r
761                                         for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
762                                         {\r
763                                                 radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );\r
764                                                 VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );\r
765                                                 rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];\r
766                                         }\r
767                                 }\r
768                                 \r
769                                 /* subdivide into area lights */\r
770                                 RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );\r
771                         }\r
772                         \r
773                         /* generate 2 tris */\r
774                         else\r
775                         {\r
776                                 rw.numVerts = 3;\r
777                                 for( t = 0; t < 2; t++ )\r
778                                 {\r
779                                         for( v = 0; v < 3 + t; v++ )\r
780                                         {\r
781                                                 /* get "other" triangle (stupid hacky logic, but whatevah) */\r
782                                                 if( v == 1 && t == 1 )\r
783                                                         v++;\r
784 \r
785                                                 /* get most everything */\r
786                                                 memcpy( &rw.verts[ v ], dv[ v ], sizeof( bspDrawVert_t ) );\r
787                                                 \r
788                                                 /* fix colors */\r
789                                                 for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
790                                                 {\r
791                                                         radVertexLuxel = RAD_VERTEX_LUXEL( i, ds->firstVert + dv[ v ]->color[ 0 ][ 0 ] );\r
792                                                         VectorCopy( radVertexLuxel, rw.verts[ v ].color[ i ] );\r
793                                                         rw.verts[ v ].color[ i ][ 3 ] = dv[ v ]->color[ i ][ 3 ];\r
794                                                 }\r
795                                         }\r
796                                         \r
797                                         /* subdivide into area lights */\r
798                                         RadSubdivideDiffuseLight( lightmapNum, ds, lm, si, scale, subdivide, qtrue, &rw, cw );\r
799                                 }\r
800                         }\r
801                 }\r
802         }\r
803         \r
804         /* free the mesh */\r
805         FreeMesh( mesh );\r
806 }\r
807 \r
808 \r
809 \r
810 \r
811 /*\r
812 RadLight()\r
813 creates unbounced diffuse lights for a given surface\r
814 */\r
815 \r
816 void RadLight( int num )\r
817 {\r
818         int                                     lightmapNum;\r
819         float                           scale, subdivide;\r
820         int                                     contentFlags, surfaceFlags, compileFlags;\r
821         bspDrawSurface_t        *ds;\r
822         surfaceInfo_t           *info;\r
823         rawLightmap_t           *lm;\r
824         shaderInfo_t            *si;\r
825         clipWork_t                      cw;\r
826         \r
827         \r
828         /* get drawsurface, lightmap, and shader info */\r
829         ds = &bspDrawSurfaces[ num ];\r
830         info = &surfaceInfos[ num ];\r
831         lm = info->lm;\r
832         si = info->si;\r
833         scale = si->bounceScale;\r
834         \r
835         /* find nodraw bit */\r
836         contentFlags = surfaceFlags = compileFlags = 0;\r
837         ApplySurfaceParm( "nodraw", &contentFlags, &surfaceFlags, &compileFlags );\r
838         \r
839         /* early outs? */\r
840         if( scale <= 0.0f || (si->compileFlags & C_SKY) || si->autosprite ||\r
841                 (bspShaders[ ds->shaderNum ].contentFlags & contentFlags) || (bspShaders[ ds->shaderNum ].surfaceFlags & surfaceFlags) ||\r
842                 (si->compileFlags & compileFlags) )\r
843                 return;\r
844         \r
845         /* determine how much we need to chop up the surface */\r
846         if( si->lightSubdivide )\r
847                 subdivide = si->lightSubdivide;\r
848         else\r
849                 subdivide = diffuseSubdivide;\r
850         \r
851         /* inc counts */\r
852         numDiffuseSurfaces++;\r
853         \r
854         /* iterate through styles (this could be more efficient, yes) */\r
855         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
856         {\r
857                 /* switch on type */\r
858                 if( ds->lightmapStyles[ lightmapNum ] != LS_NONE && ds->lightmapStyles[ lightmapNum ] != LS_UNUSED )\r
859                 {\r
860                         switch( ds->surfaceType )\r
861                         {\r
862                                 case MST_PLANAR:\r
863                                 case MST_TRIANGLE_SOUP:\r
864                                         RadLightForTriangles( num, lightmapNum, lm, si, scale, subdivide, &cw );\r
865                                         break;\r
866                                 \r
867                                 case MST_PATCH:\r
868                                         RadLightForPatch( num, lightmapNum, lm, si, scale, subdivide, &cw );\r
869                                         break;\r
870                                 \r
871                                 default:\r
872                                         break;\r
873                         }\r
874                 }\r
875         }\r
876 }\r
877 \r
878 \r
879 \r
880 /*\r
881 RadCreateDiffuseLights()\r
882 creates lights for unbounced light on surfaces in the bsp\r
883 */\r
884 \r
885 int     iterations = 0;\r
886 \r
887 void RadCreateDiffuseLights( void )\r
888 {\r
889         /* startup */\r
890         Sys_FPrintf( SYS_VRB, "--- RadCreateDiffuseLights ---\n" );\r
891         numDiffuseSurfaces = 0;\r
892         numDiffuseLights = 0;\r
893         numBrushDiffuseLights = 0;\r
894         numTriangleDiffuseLights = 0;\r
895         numPatchDiffuseLights = 0;\r
896         numAreaLights = 0;\r
897         \r
898         /* hit every surface (threaded) */\r
899         RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, RadLight );\r
900         \r
901         /* dump the lights generated to a file */\r
902         if( dump )\r
903         {\r
904                 char    dumpName[ 1024 ], ext[ 64 ];\r
905                 FILE    *file;\r
906                 light_t *light;\r
907                 \r
908                 strcpy( dumpName, source );\r
909                 StripExtension( dumpName );\r
910                 sprintf( ext, "_bounce_%03d.map", iterations );\r
911                 strcat( dumpName, ext );\r
912                 file = fopen( dumpName, "wb" );\r
913                 Sys_Printf( "Writing %s...\n", dumpName );\r
914                 if( file )\r
915                 {\r
916                         for( light = lights; light; light = light->next )\r
917                         {\r
918                                 fprintf( file,\r
919                                         "{\n"\r
920                                         "\"classname\" \"light\"\n"\r
921                                         "\"light\" \"%d\"\n"\r
922                                         "\"origin\" \"%.0f %.0f %.0f\"\n"\r
923                                         "\"_color\" \"%.3f %.3f %.3f\"\n"\r
924                                         "}\n",\r
925                                         \r
926                                         (int) light->add,\r
927                                         \r
928                                         light->origin[ 0 ],\r
929                                         light->origin[ 1 ],\r
930                                         light->origin[ 2 ],\r
931                                         \r
932                                         light->color[ 0 ],\r
933                                         light->color[ 1 ],\r
934                                         light->color[ 2 ] );\r
935                         }\r
936                         fclose( file );\r
937                 }\r
938         }\r
939         \r
940         /* increment */\r
941         iterations++;\r
942         \r
943         /* print counts */\r
944         Sys_Printf( "%8d diffuse surfaces\n", numDiffuseSurfaces );\r
945         Sys_FPrintf( SYS_VRB, "%8d total diffuse lights\n", numDiffuseLights );\r
946         Sys_FPrintf( SYS_VRB, "%8d brush diffuse lights\n", numBrushDiffuseLights );\r
947         Sys_FPrintf( SYS_VRB, "%8d patch diffuse lights\n", numPatchDiffuseLights );\r
948         Sys_FPrintf( SYS_VRB, "%8d triangle diffuse lights\n", numTriangleDiffuseLights );\r
949 }\r
950 \r
951 \r
952 \r
953 \r
954 \r