]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake3/q3map2/light.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake3 / q3map2 / light.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_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 CreateSunLight() - ydnar\r
42 this creates a sun light\r
43 */\r
44 \r
45 static void CreateSunLight( sun_t *sun )\r
46 {\r
47         int                     i;\r
48         float           photons, d, angle, elevation, da, de;\r
49         vec3_t          direction;\r
50         light_t         *light;\r
51         \r
52         \r
53         /* dummy check */\r
54         if( sun == NULL )\r
55                 return;\r
56         \r
57         /* fixup */\r
58         if( sun->numSamples < 1 )\r
59                 sun->numSamples = 1;\r
60         \r
61         /* set photons */\r
62         photons = sun->photons / sun->numSamples;\r
63         \r
64         /* create the right number of suns */\r
65         for( i = 0; i < sun->numSamples; i++ )\r
66         {\r
67                 /* calculate sun direction */\r
68                 if( i == 0 )\r
69                         VectorCopy( sun->direction, direction );\r
70                 else\r
71                 {\r
72                         /*\r
73                                 sun->direction[ 0 ] = cos( angle ) * cos( elevation );\r
74                                 sun->direction[ 1 ] = sin( angle ) * cos( elevation );\r
75                                 sun->direction[ 2 ] = sin( elevation );\r
76                                 \r
77                                 xz_dist   = sqrt( x*x + z*z )\r
78                                 latitude  = atan2( xz_dist, y ) * RADIANS\r
79                                 longitude = atan2( x,       z ) * RADIANS\r
80                         */\r
81                         \r
82                         d = sqrt( sun->direction[ 0 ] * sun->direction[ 0 ] + sun->direction[ 1 ] * sun->direction[ 1 ] );\r
83                         angle = atan2( sun->direction[ 1 ], sun->direction[ 0 ] );\r
84                         elevation = atan2( sun->direction[ 2 ], d );\r
85                         \r
86                         /* jitter the angles (loop to keep random sample within sun->deviance steridians) */\r
87                         do\r
88                         {\r
89                                 da = (Random() * 2.0f - 1.0f) * sun->deviance;\r
90                                 de = (Random() * 2.0f - 1.0f) * sun->deviance;\r
91                         }\r
92                         while( (da * da + de * de) > (sun->deviance * sun->deviance) );\r
93                         angle += da;\r
94                         elevation += de;\r
95                         \r
96                         /* debug code */\r
97                         //%     Sys_Printf( "%d: Angle: %3.4f Elevation: %3.3f\n", sun->numSamples, (angle / Q_PI * 180.0f), (elevation / Q_PI * 180.0f) );\r
98                         \r
99                         /* create new vector */\r
100                         direction[ 0 ] = cos( angle ) * cos( elevation );\r
101                         direction[ 1 ] = sin( angle ) * cos( elevation );\r
102                         direction[ 2 ] = sin( elevation );\r
103                 }\r
104                 \r
105                 /* create a light */\r
106                 numSunLights++;\r
107                 light = safe_malloc( sizeof( *light ) );\r
108                 memset( light, 0, sizeof( *light ) );\r
109                 light->next = lights;\r
110                 lights = light;\r
111                 \r
112                 /* initialize the light */\r
113                 light->flags = LIGHT_SUN_DEFAULT;\r
114                 light->type = EMIT_SUN;\r
115                 light->fade = 1.0f;\r
116                 light->falloffTolerance = falloffTolerance;\r
117                 light->filterRadius = sun->filterRadius / sun->numSamples;\r
118                 \r
119                 /* set the light's position out to infinity */\r
120                 VectorMA( vec3_origin, (MAX_WORLD_COORD * 8.0f), direction, light->origin );    /* MAX_WORLD_COORD * 2.0f */\r
121                 \r
122                 /* set the facing to be the inverse of the sun direction */\r
123                 VectorScale( direction, -1.0, light->normal );\r
124                 light->dist = DotProduct( light->origin, light->normal );\r
125                 \r
126                 /* set color and photons */\r
127                 VectorCopy( sun->color, light->color );\r
128                 light->photons = photons * skyScale;\r
129         }\r
130 \r
131         /* another sun? */\r
132         if( sun->next != NULL )\r
133                 CreateSunLight( sun->next );\r
134 }\r
135 \r
136 \r
137 \r
138 /*\r
139 CreateSkyLights() - ydnar\r
140 simulates sky light with multiple suns\r
141 */\r
142 \r
143 static void CreateSkyLights( vec3_t color, float value, int iterations, float filterRadius )\r
144 {\r
145         int                     c, i, j, k, numSuns;\r
146         float           step, start;\r
147         vec3_t          in;\r
148         sun_t           sun;\r
149         \r
150         \r
151         /* dummy check */\r
152         if( value <= 0.0f || iterations < 2 )\r
153                 return;\r
154         \r
155         /* calculate some stuff */\r
156         step = 2.0f / (iterations - 1);\r
157         start = -1.0f;\r
158         \r
159         /* basic sun setup */\r
160         VectorCopy( color, sun.color );\r
161         sun.deviance = 0.0f;\r
162         sun.filterRadius = filterRadius;\r
163         sun.numSamples = 1;\r
164         sun.next = NULL;\r
165         \r
166         /* iterate */\r
167         numSuns = 0;\r
168         for( c = 0; c < 2; c++ )\r
169         {\r
170                 for( k = 0, in[ 2 ] = start; k < iterations; k++, in[ 2 ] += step )\r
171                 {\r
172                         /* don't create sky light below the horizon */\r
173                         if( in[ 2 ] <= 0.0f )\r
174                                 continue;\r
175                         \r
176                         for( j = 0, in[ 1 ] = start; j < iterations; j++, in[ 1 ] += step )\r
177                         {\r
178                                 for( i = 0, in[ 0 ] = start; i < iterations; i++, in[ 0 ] += step )\r
179                                 {\r
180                                         if( VectorNormalize( in, sun.direction ) )\r
181                                         {\r
182                                                 if( c > 0 && numSuns > 0 )\r
183                                                 {\r
184                                                         sun.photons = value / numSuns;\r
185                                                         CreateSunLight( &sun );\r
186                                                 }\r
187                                                 else\r
188                                                         numSuns++;\r
189                                         }\r
190                                 }\r
191                         }\r
192                 }\r
193         }\r
194 }\r
195 \r
196 \r
197 \r
198 /*\r
199 CreateEntityLights()\r
200 creates lights from light entities\r
201 */\r
202 \r
203 void CreateEntityLights( void )\r
204 {\r
205         int                             i, j;\r
206         light_t                 *light, *light2;\r
207         entity_t                *e, *e2;\r
208         const char              *name;\r
209         const char              *target;\r
210         vec3_t                  dest;\r
211         const char              *_color;\r
212         float                   intensity, scale, deviance, filterRadius;\r
213         int                             spawnflags, flags, numSamples;\r
214         qboolean                junior;\r
215 \r
216         \r
217         /* go throught entity list and find lights */\r
218         for( i = 0; i < numEntities; i++ )\r
219         {\r
220                 /* get entity */\r
221                 e = &entities[ i ];\r
222                 name = ValueForKey( e, "classname" );\r
223                 \r
224                 /* ydnar: check for lightJunior */\r
225                 if( Q_strncasecmp( name, "lightJunior", 11 ) == 0 )\r
226                         junior = qtrue;\r
227                 else if( Q_strncasecmp( name, "light", 5 ) == 0 )\r
228                         junior = qfalse;\r
229                 else\r
230                         continue;\r
231                 \r
232                 /* lights with target names (and therefore styles) are only parsed from BSP */\r
233                 target = ValueForKey( e, "targetname" );\r
234                 if( target[ 0 ] != '\0' && i >= numBSPEntities )\r
235                         continue;\r
236                 \r
237                 /* create a light */\r
238                 numPointLights++;\r
239                 light = safe_malloc( sizeof( *light ) );\r
240                 memset( light, 0, sizeof( *light ) );\r
241                 light->next = lights;\r
242                 lights = light;\r
243                 \r
244                 /* handle spawnflags */\r
245                 spawnflags = IntForKey( e, "spawnflags" );\r
246                 \r
247                 /* ydnar: quake 3+ light behavior */\r
248                 if( game->wolfLight == qfalse )\r
249                 {\r
250                         /* set default flags */\r
251                         flags = LIGHT_Q3A_DEFAULT;\r
252                         \r
253                         /* linear attenuation? */\r
254                         if( spawnflags & 1 )\r
255                         {\r
256                                 flags |= LIGHT_ATTEN_LINEAR;\r
257                                 flags &= ~LIGHT_ATTEN_ANGLE;\r
258                         }\r
259                         \r
260                         /* no angle attenuate? */\r
261                         if( spawnflags & 2 )\r
262                                 flags &= ~LIGHT_ATTEN_ANGLE;\r
263                 }\r
264                 \r
265                 /* ydnar: wolf light behavior */\r
266                 else\r
267                 {\r
268                         /* set default flags */\r
269                         flags = LIGHT_WOLF_DEFAULT;\r
270                         \r
271                         /* inverse distance squared attenuation? */\r
272                         if( spawnflags & 1 )\r
273                         {\r
274                                 flags &= ~LIGHT_ATTEN_LINEAR;\r
275                                 flags |= LIGHT_ATTEN_ANGLE;\r
276                         }\r
277                         \r
278                         /* angle attenuate? */\r
279                         if( spawnflags & 2 )\r
280                                 flags |= LIGHT_ATTEN_ANGLE;\r
281                 }\r
282                 \r
283                 /* other flags (borrowed from wolf) */\r
284                 \r
285                 /* wolf dark light? */\r
286                 if( (spawnflags & 4) || (spawnflags & 8) )\r
287                         flags |= LIGHT_DARK;\r
288                 \r
289                 /* nogrid? */\r
290                 if( spawnflags & 16 )\r
291                         flags &= ~LIGHT_GRID;\r
292                 \r
293                 /* junior? */\r
294                 if( junior )\r
295                 {\r
296                         flags |= LIGHT_GRID;\r
297                         flags &= ~LIGHT_SURFACES;\r
298                 }\r
299                 \r
300                 /* store the flags */\r
301                 light->flags = flags;\r
302                 \r
303                 /* ydnar: set fade key (from wolf) */\r
304                 light->fade = 1.0f;\r
305                 if( light->flags & LIGHT_ATTEN_LINEAR )\r
306                 {\r
307                         light->fade = FloatForKey( e, "fade" );\r
308                         if( light->fade == 0.0f )\r
309                                 light->fade = 1.0f;\r
310                 }\r
311                 \r
312                 /* ydnar: set angle scaling (from vlight) */\r
313                 light->angleScale = FloatForKey( e, "_anglescale" );\r
314                 if( light->angleScale != 0.0f )\r
315                         light->flags |= LIGHT_ATTEN_ANGLE;\r
316                 \r
317                 /* set origin */\r
318                 GetVectorForKey( e, "origin", light->origin);\r
319                 light->style = IntForKey( e, "_style" );\r
320                 if( light->style == 0 )\r
321                         light->style = IntForKey( e, "style" );\r
322                 if( light->style < LS_NORMAL || light->style >= LS_NONE )\r
323                         Error( "Invalid lightstyle (%d) on entity %d", light->style, i );\r
324                 \r
325                 /* set light intensity */\r
326                 intensity = FloatForKey( e, "_light" );\r
327                 if( intensity == 0.0f )\r
328                         intensity = FloatForKey( e, "light" );\r
329                 if( intensity == 0.0f)\r
330                         intensity = 300.0f;\r
331                 \r
332                 /* ydnar: set light scale (sof2) */\r
333                 scale = FloatForKey( e, "scale" );\r
334                 if( scale == 0.0f )\r
335                         scale = 1.0f;\r
336                 intensity *= scale;\r
337                 \r
338                 /* ydnar: get deviance and samples */\r
339                 deviance = FloatForKey( e, "_deviance" );\r
340                 if( deviance == 0.0f )\r
341                         deviance = FloatForKey( e, "_deviation" );\r
342                 if( deviance == 0.0f )\r
343                         deviance = FloatForKey( e, "_jitter" );\r
344                 numSamples = IntForKey( e, "_samples" );\r
345                 if( deviance < 0.0f || numSamples < 1 )\r
346                 {\r
347                         deviance = 0.0f;\r
348                         numSamples = 1;\r
349                 }\r
350                 intensity /= numSamples;\r
351                 \r
352                 /* ydnar: get filter radius */\r
353                 filterRadius = FloatForKey( e, "_filterradius" );\r
354                 if( filterRadius == 0.0f )\r
355                         filterRadius = FloatForKey( e, "_filteradius" );\r
356                 if( filterRadius == 0.0f )\r
357                         filterRadius = FloatForKey( e, "_filter" );\r
358                 if( filterRadius < 0.0f )\r
359                         filterRadius = 0.0f;\r
360                 light->filterRadius = filterRadius;\r
361                 \r
362                 /* set light color */\r
363                 _color = ValueForKey( e, "_color" );\r
364                 if( _color && _color[ 0 ] )\r
365                 {\r
366                         sscanf( _color, "%f %f %f", &light->color[ 0 ], &light->color[ 1 ], &light->color[ 2 ] );\r
367                         ColorNormalize( light->color, light->color );\r
368                 }\r
369                 else\r
370                         light->color[ 0 ] = light->color[ 1 ] = light->color[ 2 ] = 1.0f;\r
371                 \r
372                 intensity = intensity * pointScale;\r
373                 light->photons = intensity;\r
374                 \r
375                 light->type = EMIT_POINT;\r
376                 \r
377                 /* set falloff threshold */\r
378                 light->falloffTolerance = falloffTolerance / numSamples;\r
379                 \r
380                 /* lights with a target will be spotlights */\r
381                 target = ValueForKey( e, "target" );\r
382                 if( target[ 0 ] )\r
383                 {\r
384                         float           radius;\r
385                         float           dist;\r
386                         sun_t           sun;\r
387                         const char      *_sun;\r
388                         \r
389                         \r
390                         /* get target */\r
391                         e2 = FindTargetEntity( target );\r
392                         if( e2 == NULL )\r
393                         {\r
394                                 Sys_Printf( "WARNING: light at (%i %i %i) has missing target\n",\r
395                                         (int) light->origin[ 0 ], (int) light->origin[ 1 ], (int) light->origin[ 2 ] );\r
396                         }\r
397                         else\r
398                         {\r
399                                 /* not a point light */\r
400                                 numPointLights--;\r
401                                 numSpotLights++;\r
402                                 \r
403                                 /* make a spotlight */\r
404                                 GetVectorForKey( e2, "origin", dest );\r
405                                 VectorSubtract( dest, light->origin, light->normal );\r
406                                 dist = VectorNormalize( light->normal, light->normal );\r
407                                 radius = FloatForKey( e, "radius" );\r
408                                 if( !radius )\r
409                                         radius = 64;\r
410                                 if( !dist )\r
411                                         dist = 64;\r
412                                 light->radiusByDist = (radius + 16) / dist;\r
413                                 light->type = EMIT_SPOT;\r
414                                 \r
415                                 /* ydnar: wolf mods: spotlights always use nonlinear + angle attenuation */\r
416                                 light->flags &= ~LIGHT_ATTEN_LINEAR;\r
417                                 light->flags |= LIGHT_ATTEN_ANGLE;\r
418                                 light->fade = 1.0f;\r
419                                 \r
420                                 /* ydnar: is this a sun? */\r
421                                 _sun = ValueForKey( e, "_sun" );\r
422                                 if( _sun[ 0 ] == '1' )\r
423                                 {\r
424                                         /* not a spot light */\r
425                                         numSpotLights--;\r
426                                         \r
427                                         /* unlink this light */\r
428                                         lights = light->next;\r
429                                         \r
430                                         /* make a sun */\r
431                                         VectorScale( light->normal, -1.0f, sun.direction );\r
432                                         VectorCopy( light->color, sun.color );\r
433                                         sun.photons = (intensity / pointScale);\r
434                                         sun.deviance = deviance / 180.0f * Q_PI;\r
435                                         sun.numSamples = numSamples;\r
436                                         sun.next = NULL;\r
437                                         \r
438                                         /* make a sun light */\r
439                                         CreateSunLight( &sun );\r
440                                         \r
441                                         /* free original light */\r
442                                         free( light );\r
443                                         light = NULL;\r
444                                         \r
445                                         /* skip the rest of this love story */\r
446                                         continue;\r
447                                 }\r
448                         }\r
449                 }\r
450                 \r
451                 /* jitter the light */\r
452                 for( j = 1; j < numSamples; j++ )\r
453                 {\r
454                         /* create a light */\r
455                         light2 = safe_malloc( sizeof( *light ) );\r
456                         memcpy( light2, light, sizeof( *light ) );\r
457                         light2->next = lights;\r
458                         lights = light2;\r
459                         \r
460                         /* add to counts */\r
461                         if( light->type == EMIT_SPOT )\r
462                                 numSpotLights++;\r
463                         else\r
464                                 numPointLights++;\r
465                         \r
466                         /* jitter it */\r
467                         light2->origin[ 0 ] = light->origin[ 0 ] + (Random() * 2.0f - 1.0f) * deviance;\r
468                         light2->origin[ 1 ] = light->origin[ 1 ] + (Random() * 2.0f - 1.0f) * deviance;\r
469                         light2->origin[ 2 ] = light->origin[ 2 ] + (Random() * 2.0f - 1.0f) * deviance;\r
470                 }\r
471         }\r
472 }\r
473 \r
474 \r
475 \r
476 /*\r
477 CreateSurfaceLights() - ydnar\r
478 this hijacks the radiosity code to generate surface lights for first pass\r
479 */\r
480 \r
481 #define APPROX_BOUNCE   1.0f\r
482 \r
483 void CreateSurfaceLights( void )\r
484 {\r
485         int                                     i;\r
486         bspDrawSurface_t        *ds;\r
487         surfaceInfo_t           *info;\r
488         shaderInfo_t            *si;\r
489         light_t                         *light;\r
490         float                           subdivide;\r
491         vec3_t                          origin;\r
492         clipWork_t                      cw;\r
493         const char                      *nss;\r
494         \r
495         \r
496         /* get sun shader supressor */\r
497         nss = ValueForKey( &entities[ 0 ], "_noshadersun" );\r
498         \r
499         /* walk the list of surfaces */\r
500         for( i = 0; i < numBSPDrawSurfaces; i++ )\r
501         {\r
502                 /* get surface and other bits */\r
503                 ds = &bspDrawSurfaces[ i ];\r
504                 info = &surfaceInfos[ i ];\r
505                 si = info->si;\r
506                 \r
507                 /* sunlight? */\r
508                 if( si->sun != NULL && nss[ 0 ] != '1' )\r
509                 {\r
510                         Sys_FPrintf( SYS_VRB, "Sun: %s\n", si->shader );\r
511                         CreateSunLight( si->sun );\r
512                         si->sun = NULL; /* FIXME: leak! */\r
513                 }\r
514                 \r
515                 /* sky light? */\r
516                 if( si->skyLightValue > 0.0f )\r
517                 {\r
518                         Sys_FPrintf( SYS_VRB, "Sky: %s\n", si->shader );\r
519                         CreateSkyLights( si->color, si->skyLightValue, si->skyLightIterations, si->lightFilterRadius );\r
520                         si->skyLightValue = 0.0f;       /* FIXME: hack! */\r
521                 }\r
522                 \r
523                 /* try to early out */\r
524                 if( si->value <= 0 )\r
525                         continue;\r
526                 \r
527                 /* autosprite shaders become point lights */\r
528                 if( si->autosprite )\r
529                 {\r
530                         /* create an average xyz */\r
531                         VectorAdd( info->mins, info->maxs, origin );\r
532                         VectorScale( origin, 0.5f, origin );\r
533                         \r
534                         /* create a light */\r
535                         light = safe_malloc( sizeof( *light ) );\r
536                         memset( light, 0, sizeof( *light ) );\r
537                         light->next = lights;\r
538                         lights = light;\r
539                         \r
540                         /* set it up */\r
541                         light->flags = LIGHT_Q3A_DEFAULT;\r
542                         light->type = EMIT_POINT;\r
543                         light->photons = si->value * pointScale;\r
544                         light->fade = 1.0f;\r
545                         light->si = si;\r
546                         VectorCopy( origin, light->origin );\r
547                         VectorCopy( si->color, light->color );\r
548                         light->falloffTolerance = falloffTolerance;\r
549                         light->style = light->style;\r
550                         \r
551                         /* add to point light count and continue */\r
552                         numPointLights++;\r
553                         continue;\r
554                 }\r
555                 \r
556                 /* get subdivision amount */\r
557                 if( si->lightSubdivide > 0 )\r
558                         subdivide = si->lightSubdivide;\r
559                 else\r
560                         subdivide = defaultLightSubdivide;\r
561                 \r
562                 /* switch on type */\r
563                 switch( ds->surfaceType )\r
564                 {\r
565                         case MST_PLANAR:\r
566                         case MST_TRIANGLE_SOUP:\r
567                                 RadLightForTriangles( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );\r
568                                 break;\r
569                         \r
570                         case MST_PATCH:\r
571                                 RadLightForPatch( i, 0, info->lm, si, APPROX_BOUNCE, subdivide, &cw );\r
572                                 break;\r
573                         \r
574                         default:\r
575                                 break;\r
576                 }\r
577         }\r
578 }\r
579 \r
580 \r
581 \r
582 /*\r
583 SetEntityOrigins()\r
584 find the offset values for inline models\r
585 */\r
586 \r
587 void SetEntityOrigins( void )\r
588 {\r
589         int                                     i, j, k, f;\r
590         entity_t                        *e;\r
591         vec3_t                          origin;\r
592         const char                      *key;\r
593         int                                     modelnum;\r
594         bspModel_t                      *dm;\r
595         bspDrawSurface_t        *ds;\r
596         \r
597         \r
598         /* ydnar: copy drawverts into private storage for nefarious purposes */\r
599         yDrawVerts = safe_malloc( numBSPDrawVerts * sizeof( bspDrawVert_t ) );\r
600         memcpy( yDrawVerts, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVert_t ) );\r
601         \r
602         /* set the entity origins */\r
603         for( i = 0; i < numEntities; i++ )\r
604         {\r
605                 /* get entity and model */\r
606                 e = &entities[ i ];\r
607                 key = ValueForKey( e, "model" );\r
608                 if( key[ 0 ] != '*' )\r
609                         continue;\r
610                 modelnum = atoi( key + 1 );\r
611                 dm = &bspModels[ modelnum ];\r
612                 \r
613                 /* get entity origin */\r
614                 key = ValueForKey( e, "origin" );\r
615                 if( key[ 0 ] == '\0' )\r
616                         continue;\r
617                 GetVectorForKey( e, "origin", origin );\r
618                 \r
619                 /* set origin for all surfaces for this model */\r
620                 for( j = 0; j < dm->numBSPSurfaces; j++ )\r
621                 {\r
622                         /* get drawsurf */\r
623                         ds = &bspDrawSurfaces[ dm->firstBSPSurface + j ];\r
624                         \r
625                         /* set its verts */\r
626                         for( k = 0; k < ds->numVerts; k++ )\r
627                         {\r
628                                 f = ds->firstVert + k;\r
629                                 VectorAdd( origin, bspDrawVerts[ f ].xyz, yDrawVerts[ f ].xyz );\r
630                         }\r
631                 }\r
632         }\r
633 }\r
634 \r
635 \r
636 \r
637 /*\r
638 PointToPolygonFormFactor()\r
639 calculates the area over a point/normal hemisphere a winding covers\r
640 ydnar: fixme: there has to be a faster way to calculate this\r
641 without the expensive per-vert sqrts and transcendental functions\r
642 ydnar 2002-09-30: added -faster switch because only 19% deviance > 10%\r
643 between this and the approximation\r
644 */\r
645 \r
646 #define ONE_OVER_2PI    0.159154942f    //% (1.0f / (2.0f * 3.141592657f))\r
647 \r
648 float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w )\r
649 {\r
650         vec3_t          triVector, triNormal;\r
651         int                     i, j;\r
652         vec3_t          dirs[ MAX_POINTS_ON_WINDING ];\r
653         float           total;\r
654         float           dot, angle, facing;\r
655         \r
656         \r
657         /* this is expensive */\r
658         for( i = 0; i < w->numpoints; i++ )\r
659         {\r
660                 VectorSubtract( w->p[ i ], point, dirs[ i ] );\r
661                 VectorNormalize( dirs[ i ], dirs[ i ] );\r
662         }\r
663         \r
664         /* duplicate first vertex to avoid mod operation */\r
665         VectorCopy( dirs[ 0 ], dirs[ i ] );\r
666         \r
667         /* calculcate relative area */\r
668         total = 0.0f;\r
669         for( i = 0; i < w->numpoints; i++ )\r
670         {\r
671                 /* get a triangle */\r
672                 j = i + 1;\r
673                 dot = DotProduct( dirs[ i ], dirs[ j ] );\r
674                 \r
675                 /* roundoff can cause slight creep, which gives an IND from acos */\r
676                 if( dot > 1.0f )\r
677                         dot = 1.0f;\r
678                 else if( dot < -1.0f )\r
679                         dot = -1.0f;\r
680                 \r
681                 /* get the angle */\r
682                 angle = acos( dot );\r
683                 \r
684                 CrossProduct( dirs[ i ], dirs[ j ], triVector );\r
685                 if( VectorNormalize( triVector, triNormal ) < 0.0001f )\r
686                         continue;\r
687                 \r
688                 facing = DotProduct( normal, triNormal );\r
689                 total += facing * angle;\r
690                 \r
691                 /* ydnar: this was throwing too many errors with radiosity + crappy maps. ignoring it. */\r
692                 if( total > 6.3f || total < -6.3f )\r
693                         return 0.0f;\r
694         }\r
695         \r
696         /* now in the range of 0 to 1 over the entire incoming hemisphere */\r
697         //%     total /= (2.0f * 3.141592657f);\r
698         total *= ONE_OVER_2PI;\r
699         return total;\r
700 }\r
701 \r
702 \r
703 \r
704 /*\r
705 LightContributionTosample()\r
706 determines the amount of light reaching a sample (luxel or vertex) from a given light\r
707 */\r
708 \r
709 int LightContributionToSample( trace_t *trace )\r
710 {\r
711         light_t                 *light;\r
712         float                   angle;\r
713         float                   add;\r
714         float                   dist;\r
715         \r
716         \r
717         /* get light */\r
718         light = trace->light;\r
719         \r
720         /* clear color */\r
721         VectorClear( trace->color );\r
722         \r
723         /* ydnar: early out */\r
724         if( !(light->flags & LIGHT_SURFACES) || light->envelope <= 0.0f )\r
725                 return 0;\r
726         \r
727         /* do some culling checks */\r
728         if( light->type != EMIT_SUN )\r
729         {\r
730                 /* MrE: if the light is behind the surface */\r
731                 if( trace->twoSided == qfalse )\r
732                         if( DotProduct( light->origin, trace->normal ) - DotProduct( trace->origin, trace->normal ) < 0.0f )\r
733                                 return 0;\r
734                 \r
735                 /* ydnar: test pvs */\r
736                 if( !ClusterVisible( trace->cluster, light->cluster ) )\r
737                         return 0;\r
738         }\r
739         \r
740         /* ptpff approximation */\r
741         if( light->type == EMIT_AREA && faster )\r
742         {\r
743                 /* get direction and distance */\r
744                 VectorCopy( light->origin, trace->end );\r
745                 dist = SetupTrace( trace );\r
746                 if( dist >= light->envelope )\r
747                         return 0;\r
748                 \r
749                 /* clamp the distance to prevent super hot spots */\r
750                 if( dist < 16.0f )\r
751                         dist = 16.0f;\r
752                 \r
753                 /* angle attenuation */\r
754                 angle = DotProduct( trace->normal, trace->direction );\r
755                 \r
756                 /* twosided lighting */\r
757                 if( trace->twoSided )\r
758                         angle = fabs( angle );\r
759                 \r
760                 /* attenuate */\r
761                 angle *= -DotProduct( light->normal, trace->direction );\r
762                 if( angle <= 0.0f )\r
763                         return 0;\r
764                 add = light->photons / (dist * dist) * angle;\r
765         }\r
766         \r
767         /* exact point to polygon form factor */\r
768         else if( light->type == EMIT_AREA )\r
769         {\r
770                 float           factor;\r
771                 float           d;\r
772                 vec3_t          pushedOrigin;\r
773                 \r
774                 \r
775                 /* project sample point into light plane */\r
776                 d = DotProduct( trace->origin, light->normal ) - light->dist;\r
777                 //%     if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )\r
778                 //%             return 0;\r
779                 if( d < 3.0f )\r
780                 {\r
781                         /* sample point behind plane? */\r
782                         if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )\r
783                                 return 0;\r
784                         \r
785                         /* sample plane coincident? */\r
786                         if( d > -3.0f && DotProduct( trace->normal, light->normal ) > 0.9f )\r
787                                 return 0;\r
788                 }\r
789                 \r
790                 /* nudge the point so that it is clearly forward of the light */\r
791                 /* so that surfaces meeting a light emiter don't get black edges */\r
792                 if( d > -8.0f && d < 8.0f )\r
793                         VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );                             \r
794                 else\r
795                         VectorCopy( trace->origin, pushedOrigin );\r
796                 \r
797                 /* get direction and distance */\r
798                 VectorCopy( light->origin, trace->end );\r
799                 dist = SetupTrace( trace );\r
800                 if( dist >= light->envelope )\r
801                         return 0;\r
802                 \r
803                 /* calculate the contribution */\r
804                 factor = PointToPolygonFormFactor( pushedOrigin, trace->normal, light->w );\r
805                 if( factor == 0.0f )\r
806                         return 0;\r
807                 else if( factor < 0.0f )\r
808                 {\r
809                         /* twosided lighting */\r
810                         if( trace->twoSided || (light->flags & LIGHT_TWOSIDED) )\r
811                         {\r
812                                 factor = -factor;\r
813 \r
814                                 /* push light origin to other side of the plane */\r
815                                 VectorMA( light->origin, -2.0f, light->normal, trace->end );\r
816                                 dist = SetupTrace( trace );\r
817                                 if( dist >= light->envelope )\r
818                                         return 0;\r
819                         }\r
820                         else\r
821                                 return 0;\r
822                 }\r
823                 \r
824                 /* ydnar: moved to here */\r
825                 add = factor * light->add;\r
826         }\r
827         \r
828         /* point/spot lights */\r
829         else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )\r
830         {\r
831                 /* get direction and distance */\r
832                 VectorCopy( light->origin, trace->end );\r
833                 dist = SetupTrace( trace );\r
834                 if( dist >= light->envelope )\r
835                         return 0;\r
836                 \r
837                 /* clamp the distance to prevent super hot spots */\r
838                 if( dist < 16.0f )\r
839                         dist = 16.0f;\r
840                 \r
841                 /* angle attenuation */\r
842                 angle = (light->flags & LIGHT_ATTEN_ANGLE) ? DotProduct( trace->normal, trace->direction ) : 1.0f;\r
843                 if( light->angleScale != 0.0f )\r
844                 {\r
845                         angle /= light->angleScale;\r
846                         if( angle > 1.0f )\r
847                                 angle = 1.0f;\r
848                 }\r
849                 \r
850                 /* twosided lighting */\r
851                 if( trace->twoSided )\r
852                         angle = fabs( angle );\r
853                 \r
854                 /* attenuate */\r
855                 if( light->flags & LIGHT_ATTEN_LINEAR )\r
856                 {\r
857                         add = angle * light->photons * linearScale - (dist * light->fade);\r
858                         if( add < 0.0f )\r
859                                 add = 0.0f;\r
860                 }\r
861                 else\r
862                         add = light->photons / (dist * dist) * angle;\r
863                 \r
864                 /* handle spotlights */\r
865                 if( light->type == EMIT_SPOT )\r
866                 {\r
867                         float   distByNormal, radiusAtDist, sampleRadius;\r
868                         vec3_t  pointAtDist, distToSample;\r
869                         \r
870                         \r
871                         /* do cone calculation */\r
872                         distByNormal = -DotProduct( trace->displacement, light->normal );\r
873                         if( distByNormal < 0.0f )\r
874                                 return 0;\r
875                         VectorMA( light->origin, distByNormal, light->normal, pointAtDist );\r
876                         radiusAtDist = light->radiusByDist * distByNormal;\r
877                         VectorSubtract( trace->origin, pointAtDist, distToSample );\r
878                         sampleRadius = VectorLength( distToSample );\r
879                         \r
880                         /* outside the cone */\r
881                         if( sampleRadius >= radiusAtDist )\r
882                                 return 0;\r
883                         \r
884                         /* attenuate */\r
885                         if( sampleRadius > (radiusAtDist - 32.0f) )\r
886                                 add *= ((radiusAtDist - sampleRadius) / 32.0f);\r
887                 }\r
888         }\r
889         \r
890         /* ydnar: sunlight */\r
891         else if( light->type == EMIT_SUN )\r
892         {\r
893                 /* get origin and direction */\r
894                 VectorAdd( trace->origin, light->origin, trace->end );\r
895                 dist = SetupTrace( trace );\r
896                 \r
897                 /* angle attenuation */\r
898                 angle = (light->flags & LIGHT_ATTEN_ANGLE)\r
899                         ? DotProduct( trace->normal, trace->direction )\r
900                         : 1.0f;\r
901                 \r
902                 /* twosided lighting */\r
903                 if( trace->twoSided )\r
904                         angle = fabs( angle );\r
905                 \r
906                 /* attenuate */\r
907                 add = light->photons * angle;\r
908                 if( add <= 0.0f )\r
909                         return 0;\r
910                 \r
911                 /* setup trace */\r
912                 trace->testAll = qtrue;\r
913                 VectorScale( light->color, add, trace->color );\r
914                 \r
915                 /* trace to point */\r
916                 if( trace->testOcclusion && !trace->forceSunlight )\r
917                 {\r
918                         /* trace */\r
919                         TraceLine( trace );\r
920                         if( !(trace->compileFlags & C_SKY) || trace->opaque )\r
921                         {\r
922                                 VectorClear( trace->color );\r
923                                 return -1;\r
924                         }\r
925                 }\r
926                 \r
927                 /* return to sender */\r
928                 return 1;\r
929         }\r
930         \r
931         /* ydnar: changed to a variable number */\r
932         if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )\r
933                 return 0;\r
934         \r
935         /* setup trace */\r
936         trace->testAll = qfalse;\r
937         VectorScale( light->color, add, trace->color );\r
938         \r
939         /* raytrace */\r
940         TraceLine( trace );\r
941         if( trace->passSolid || trace->opaque )\r
942         {\r
943                 VectorClear( trace->color );\r
944                 return -1;\r
945         }\r
946         \r
947         /* return to sender */\r
948         return 1;\r
949 }\r
950 \r
951 \r
952 \r
953 /*\r
954 LightingAtSample()\r
955 determines the amount of light reaching a sample (luxel or vertex)\r
956 */\r
957 \r
958 void LightingAtSample( trace_t *trace, byte styles[ MAX_LIGHTMAPS ], vec3_t colors[ MAX_LIGHTMAPS ] )\r
959 {\r
960         int                             i, lightmapNum;\r
961         \r
962         \r
963         /* clear colors */\r
964         for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
965                 VectorClear( colors[ lightmapNum ] );\r
966         \r
967         /* ydnar: normalmap */\r
968         if( normalmap )\r
969         {\r
970                 colors[ 0 ][ 0 ] = (trace->normal[ 0 ] + 1.0f) * 127.5f;\r
971                 colors[ 0 ][ 1 ] = (trace->normal[ 1 ] + 1.0f) * 127.5f;\r
972                 colors[ 0 ][ 2 ] = (trace->normal[ 2 ] + 1.0f) * 127.5f;\r
973                 return;\r
974         }\r
975         \r
976         /* ydnar: don't bounce ambient all the time */\r
977         if( !bouncing )\r
978                 VectorCopy( ambientColor, colors[ 0 ] );\r
979         \r
980         /* ydnar: trace to all the list of lights pre-stored in tw */\r
981         for( i = 0; i < trace->numLights && trace->lights[ i ] != NULL; i++ )\r
982         {\r
983                 /* set light */\r
984                 trace->light = trace->lights[ i ];\r
985                 \r
986                 /* style check */\r
987                 for( lightmapNum = 0; lightmapNum < MAX_LIGHTMAPS; lightmapNum++ )\r
988                 {\r
989                         if( styles[ lightmapNum ] == trace->light->style ||\r
990                                 styles[ lightmapNum ] == LS_NONE )\r
991                                 break;\r
992                 }\r
993                 \r
994                 /* max of MAX_LIGHTMAPS (4) styles allowed to hit a sample */\r
995                 if( lightmapNum >= MAX_LIGHTMAPS )\r
996                         continue;\r
997                 \r
998                 /* sample light */\r
999                 LightContributionToSample( trace );\r
1000                 if( trace->color[ 0 ] == 0.0f && trace->color[ 1 ] == 0.0f && trace->color[ 2 ] == 0.0f )\r
1001                         continue;\r
1002                 \r
1003                 /* handle negative light */\r
1004                 if( trace->light->flags & LIGHT_NEGATIVE )\r
1005                         VectorScale( trace->color, -1.0f, trace->color );\r
1006                 \r
1007                 /* set style */\r
1008                 styles[ lightmapNum ] = trace->light->style;\r
1009                 \r
1010                 /* add it */\r
1011                 VectorAdd( colors[ lightmapNum ], trace->color, colors[ lightmapNum ] );\r
1012                 \r
1013                 /* cheap mode */\r
1014                 if( cheap &&\r
1015                         colors[ 0 ][ 0 ] >= 255.0f &&\r
1016                         colors[ 0 ][ 1 ] >= 255.0f &&\r
1017                         colors[ 0 ][ 2 ] >= 255.0f )\r
1018                         break;\r
1019         }\r
1020 }\r
1021 \r
1022 \r
1023 \r
1024 /*\r
1025 LightContributionToPoint()\r
1026 for a given light, how much light/color reaches a given point in space (with no facing)\r
1027 note: this is similar to LightContributionToSample() but optimized for omnidirectional sampling\r
1028 */\r
1029 \r
1030 int LightContributionToPoint( trace_t *trace )\r
1031 {\r
1032         light_t         *light;\r
1033         float           add, dist;\r
1034         \r
1035         \r
1036         /* get light */\r
1037         light = trace->light;\r
1038         \r
1039         /* clear color */\r
1040         VectorClear( trace->color );\r
1041         \r
1042         /* ydnar: early out */\r
1043         if( !(light->flags & LIGHT_GRID) || light->envelope <= 0.0f )\r
1044                 return qfalse;\r
1045         \r
1046         /* is this a sun? */\r
1047         if( light->type != EMIT_SUN )\r
1048         {\r
1049                 /* sun only? */\r
1050                 if( sunOnly )\r
1051                         return qfalse;\r
1052                 \r
1053                 /* test pvs */\r
1054                 if( !ClusterVisible( trace->cluster, light->cluster ) )\r
1055                         return qfalse;\r
1056         }\r
1057         \r
1058         /* ydnar: check origin against light's pvs envelope */\r
1059         if( trace->origin[ 0 ] > light->maxs[ 0 ] || trace->origin[ 0 ] < light->mins[ 0 ] ||\r
1060                 trace->origin[ 1 ] > light->maxs[ 1 ] || trace->origin[ 1 ] < light->mins[ 1 ] ||\r
1061                 trace->origin[ 2 ] > light->maxs[ 2 ] || trace->origin[ 2 ] < light->mins[ 2 ] )\r
1062         {\r
1063                 gridBoundsCulled++;\r
1064                 return qfalse;\r
1065         }\r
1066         \r
1067         /* set light origin */\r
1068         if( light->type == EMIT_SUN )\r
1069                 VectorAdd( trace->origin, light->origin, trace->end );\r
1070         else\r
1071                 VectorCopy( light->origin, trace->end );\r
1072         \r
1073         /* set direction */\r
1074         dist = SetupTrace( trace );\r
1075         \r
1076         /* test envelope */\r
1077         if( dist > light->envelope )\r
1078         {\r
1079                 gridEnvelopeCulled++;\r
1080                 return qfalse;\r
1081         }\r
1082         \r
1083         /* ptpff approximation */\r
1084         if( light->type == EMIT_AREA && faster )\r
1085         {\r
1086                 /* clamp the distance to prevent super hot spots */\r
1087                 if( dist < 16.0f )\r
1088                         dist = 16.0f;\r
1089                 \r
1090                 /* attenuate */\r
1091                 add = light->photons / (dist * dist);\r
1092         }\r
1093         \r
1094         /* exact point to polygon form factor */\r
1095         else if( light->type == EMIT_AREA )\r
1096         {\r
1097                 float           factor, d;\r
1098                 vec3_t          pushedOrigin;\r
1099                 \r
1100                 \r
1101                 /* see if the point is behind the light */\r
1102                 d = DotProduct( trace->origin, light->normal ) - light->dist;\r
1103                 if( !(light->flags & LIGHT_TWOSIDED) && d < -1.0f )\r
1104                         return qfalse;\r
1105                 \r
1106                 /* nudge the point so that it is clearly forward of the light */\r
1107                 /* so that surfaces meeting a light emiter don't get black edges */\r
1108                 if( d > -8.0f && d < 8.0f )\r
1109                         VectorMA( trace->origin, (8.0f - d), light->normal, pushedOrigin );                             \r
1110                 else\r
1111                         VectorCopy( trace->origin, pushedOrigin );\r
1112                 \r
1113                 /* calculate the contribution (ydnar 2002-10-21: [bug 642] bad normal calc) */\r
1114                 factor = PointToPolygonFormFactor( pushedOrigin, trace->direction, light->w );\r
1115                 if( factor == 0.0f )\r
1116                         return qfalse;\r
1117                 else if( factor < 0.0f )\r
1118                 {\r
1119                         if( light->flags & LIGHT_TWOSIDED )\r
1120                                 factor = -factor;\r
1121                         else\r
1122                                 return qfalse;\r
1123                 }\r
1124                 \r
1125                 /* ydnar: moved to here */\r
1126                 add = factor * light->add;\r
1127         }\r
1128         \r
1129         /* point/spot lights */\r
1130         else if( light->type == EMIT_POINT || light->type == EMIT_SPOT )\r
1131         {\r
1132                 /* clamp the distance to prevent super hot spots */\r
1133                 if( dist < 16.0f )\r
1134                         dist = 16.0f;\r
1135                 \r
1136                 /* attenuate */\r
1137                 if( light->flags & LIGHT_ATTEN_LINEAR )\r
1138                 {\r
1139                         add = light->photons * linearScale - (dist * light->fade);\r
1140                         if( add < 0.0f )\r
1141                                 add = 0.0f;\r
1142                 }\r
1143                 else\r
1144                         add = light->photons / (dist * dist);\r
1145                 \r
1146                 /* handle spotlights */\r
1147                 if( light->type == EMIT_SPOT )\r
1148                 {\r
1149                         float   distByNormal, radiusAtDist, sampleRadius;\r
1150                         vec3_t  pointAtDist, distToSample;\r
1151                         \r
1152                         \r
1153                         /* do cone calculation */\r
1154                         distByNormal = -DotProduct( trace->displacement, light->normal );\r
1155                         if( distByNormal < 0.0f )\r
1156                                 return qfalse;\r
1157                         VectorMA( light->origin, distByNormal, light->normal, pointAtDist );\r
1158                         radiusAtDist = light->radiusByDist * distByNormal;\r
1159                         VectorSubtract( trace->origin, pointAtDist, distToSample );\r
1160                         sampleRadius = VectorLength( distToSample );\r
1161                         \r
1162                         /* outside the cone */\r
1163                         if( sampleRadius >= radiusAtDist )\r
1164                                 return qfalse;\r
1165                         \r
1166                         /* attenuate */\r
1167                         if( sampleRadius > (radiusAtDist - 32.0f) )\r
1168                                 add *= ((radiusAtDist - sampleRadius) / 32.0f);\r
1169                 }\r
1170         }\r
1171         \r
1172         /* ydnar: sunlight */\r
1173         else if( light->type == EMIT_SUN )\r
1174         {\r
1175                 /* attenuate */\r
1176                 add = light->photons;\r
1177                 if( add <= 0.0f )\r
1178                         return qfalse;\r
1179                 \r
1180                 /* setup trace */\r
1181                 trace->testAll = qtrue;\r
1182                 VectorScale( light->color, add, trace->color );\r
1183                 \r
1184                 /* trace to point */\r
1185                 if( trace->testOcclusion && !trace->forceSunlight )\r
1186                 {\r
1187                         /* trace */\r
1188                         TraceLine( trace );\r
1189                         if( !(trace->compileFlags & C_SKY) || trace->opaque )\r
1190                         {\r
1191                                 VectorClear( trace->color );\r
1192                                 return -1;\r
1193                         }\r
1194                 }\r
1195                 \r
1196                 /* return to sender */\r
1197                 return qtrue;\r
1198         }\r
1199         \r
1200         /* unknown light type */\r
1201         else\r
1202                 return qfalse;\r
1203         \r
1204         /* ydnar: changed to a variable number */\r
1205         if( add <= 0.0f || (add <= light->falloffTolerance && (light->flags & LIGHT_FAST_ACTUAL)) )\r
1206                 return qfalse;\r
1207         \r
1208         /* setup trace */\r
1209         trace->testAll = qfalse;\r
1210         VectorScale( light->color, add, trace->color );\r
1211         \r
1212         /* trace */\r
1213         TraceLine( trace );\r
1214         if( trace->passSolid )\r
1215         {\r
1216                 VectorClear( trace->color );\r
1217                 return qfalse;\r
1218         }\r
1219         \r
1220         /* we have a valid sample */\r
1221         return qtrue;\r
1222 }\r
1223 \r
1224 \r
1225 \r
1226 /*\r
1227 TraceGrid()\r
1228 grid samples are for quickly determining the lighting\r
1229 of dynamically placed entities in the world\r
1230 */\r
1231 \r
1232 #define MAX_CONTRIBUTIONS       1024\r
1233 \r
1234 typedef struct\r
1235 {\r
1236         vec3_t          dir;\r
1237         vec3_t          color;\r
1238         int                     style;\r
1239 }\r
1240 contribution_t;\r
1241 \r
1242 void TraceGrid( int num )\r
1243 {\r
1244         int                                             i, j, x, y, z, mod, step, numCon, numStyles;\r
1245         float                                   d;\r
1246         vec3_t                                  baseOrigin, cheapColor, color;\r
1247         rawGridPoint_t                  *gp;\r
1248         bspGridPoint_t                  *bgp;\r
1249         contribution_t                  contributions[ MAX_CONTRIBUTIONS ];\r
1250         trace_t                                 trace;\r
1251         \r
1252         \r
1253         /* get grid points */\r
1254         gp = &rawGridPoints[ num ];\r
1255         bgp = &bspGridPoints[ num ];\r
1256         \r
1257         /* get grid origin */\r
1258         mod = num;\r
1259         z = mod / (gridBounds[ 0 ] * gridBounds[ 1 ]);\r
1260         mod -= z * (gridBounds[ 0 ] * gridBounds[ 1 ]);\r
1261         y = mod / gridBounds[ 0 ];\r
1262         mod -= y * gridBounds[ 0 ];\r
1263         x = mod;\r
1264         \r
1265         trace.origin[ 0 ] = gridMins[ 0 ] + x * gridSize[ 0 ];\r
1266         trace.origin[ 1 ] = gridMins[ 1 ] + y * gridSize[ 1 ];\r
1267         trace.origin[ 2 ] = gridMins[ 2 ] + z * gridSize[ 2 ];\r
1268         \r
1269         /* set inhibit sphere */\r
1270         if( gridSize[ 0 ] > gridSize[ 1 ] && gridSize[ 0 ] > gridSize[ 2 ] )\r
1271                 trace.inhibitRadius = gridSize[ 0 ] * 0.5f;\r
1272         else if( gridSize[ 1 ] > gridSize[ 0 ] && gridSize[ 1 ] > gridSize[ 2 ] )\r
1273                 trace.inhibitRadius = gridSize[ 1 ] * 0.5f;\r
1274         else\r
1275                 trace.inhibitRadius = gridSize[ 2 ] * 0.5f;\r
1276         \r
1277         /* find point cluster */\r
1278         trace.cluster = ClusterForPointExt( trace.origin, GRID_EPSILON );\r
1279         if( trace.cluster < 0 )\r
1280         {\r
1281                 /* try to nudge the origin around to find a valid point */\r
1282                 VectorCopy( trace.origin, baseOrigin );\r
1283                 for( step = 9; step <= 18; step += 9 )\r
1284                 {\r
1285                         for( i = 0; i < 8; i++ )\r
1286                         {\r
1287                                 VectorCopy( baseOrigin, trace.origin );\r
1288                                 if( i & 1 )\r
1289                                         trace.origin[ 0 ] += step;\r
1290                                 else\r
1291                                         trace.origin[ 0 ] -= step;\r
1292                                 \r
1293                                 if( i & 2 )\r
1294                                         trace.origin[ 1 ] += step;\r
1295                                 else\r
1296                                         trace.origin[ 1 ] -= step;\r
1297                                 \r
1298                                 if( i & 4 )\r
1299                                         trace.origin[ 2 ] += step;\r
1300                                 else\r
1301                                         trace.origin[ 2 ] -= step;\r
1302                                 \r
1303                                 /* ydnar: changed to find cluster num */\r
1304                                 trace.cluster = ClusterForPointExt( trace.origin, VERTEX_EPSILON );\r
1305                                 if( trace.cluster >= 0 )\r
1306                                         break;\r
1307                         }\r
1308                         \r
1309                         if( i != 8 )\r
1310                                 break;\r
1311                 }\r
1312                 \r
1313                 /* can't find a valid point at all */\r
1314                 if( step > 18 )\r
1315                         return;\r
1316         }\r
1317         \r
1318         /* setup trace */\r
1319         trace.testOcclusion = !noTrace;\r
1320         trace.forceSunlight = qfalse;\r
1321         trace.recvShadows = WORLDSPAWN_RECV_SHADOWS;\r
1322         trace.numSurfaces = 0;\r
1323         trace.surfaces = NULL;\r
1324         trace.numLights = 0;\r
1325         trace.lights = NULL;\r
1326         \r
1327         /* clear */\r
1328         numCon = 0;\r
1329         VectorClear( cheapColor );\r
1330         \r
1331         /* trace to all the lights, find the major light direction, and divide the\r
1332            total light between that along the direction and the remaining in the ambient */\r
1333         for( trace.light = lights; trace.light != NULL; trace.light = trace.light->next )\r
1334         {\r
1335                 float           addSize;\r
1336                 \r
1337                 \r
1338                 /* sample light */\r
1339                 if( !LightContributionToPoint( &trace ) )\r
1340                         continue;\r
1341                 \r
1342                 /* handle negative light */\r
1343                 if( trace.light->flags & LIGHT_NEGATIVE )\r
1344                         VectorScale( trace.color, -1.0f, trace.color );\r
1345                 \r
1346                 /* add a contribution */\r
1347                 VectorCopy( trace.color, contributions[ numCon ].color );\r
1348                 VectorCopy( trace.direction, contributions[ numCon ].dir );\r
1349                 contributions[ numCon ].style = trace.light->style;\r
1350                 numCon++;\r
1351                 \r
1352                 /* push average direction around */\r
1353                 addSize = VectorLength( trace.color );\r
1354                 VectorMA( gp->dir, addSize, trace.direction, gp->dir );\r
1355                 \r
1356                 /* stop after a while */\r
1357                 if( numCon >= (MAX_CONTRIBUTIONS - 1) )\r
1358                         break;\r
1359                 \r
1360                 /* ydnar: cheap mode */\r
1361                 VectorAdd( cheapColor, trace.color, cheapColor );\r
1362                 if( cheapgrid && cheapColor[ 0 ] >= 255.0f && cheapColor[ 1 ] >= 255.0f && cheapColor[ 2 ] >= 255.0f )\r
1363                         break;\r
1364         }\r
1365         \r
1366         /* normalize to get primary light direction */\r
1367         VectorNormalize( gp->dir, gp->dir );\r
1368         \r
1369         /* now that we have identified the primary light direction,\r
1370            go back and separate all the light into directed and ambient */\r
1371         numStyles = 1;\r
1372         for( i = 0; i < numCon; i++ )\r
1373         {\r
1374                 /* get relative directed strength */\r
1375                 d = DotProduct( contributions[ i ].dir, gp->dir );\r
1376                 if( d < 0.0f )\r
1377                         d = 0.0f;\r
1378                 \r
1379                 /* find appropriate style */\r
1380                 for( j = 0; j < numStyles; j++ )\r
1381                 {\r
1382                         if( gp->styles[ j ] == contributions[ i ].style )\r
1383                                 break;\r
1384                 }\r
1385                 \r
1386                 /* style not found? */\r
1387                 if( j >= numStyles )\r
1388                 {\r
1389                         /* add a new style */\r
1390                         if( numStyles < MAX_LIGHTMAPS )\r
1391                         {\r
1392                                 gp->styles[ numStyles ] = contributions[ i ].style;\r
1393                                 bgp->styles[ numStyles ] = contributions[ i ].style;\r
1394                                 numStyles++;\r
1395                                 //%     Sys_Printf( "(%d, %d) ", num, contributions[ i ].style );\r
1396                         }\r
1397                         \r
1398                         /* fallback */\r
1399                         else\r
1400                                 j = 0;\r
1401                 }\r
1402                 \r
1403                 /* add the directed color */\r
1404                 VectorMA( gp->directed[ j ], d, contributions[ i ].color, gp->directed[ j ] );\r
1405                 \r
1406                 /* ambient light will be at 1/4 the value of directed light */\r
1407                 /* (ydnar: nuke this in favor of more dramatic lighting?) */\r
1408                 d = 0.25f * (1.0f - d);\r
1409                 VectorMA( gp->ambient[ j ], d, contributions[ i ].color, gp->ambient[ j ] );\r
1410         }\r
1411         \r
1412         \r
1413         /* store off sample */\r
1414         for( i = 0; i < MAX_LIGHTMAPS; i++ )\r
1415         {\r
1416                 /* do some fudging to keep the ambient from being too low (2003-07-05: 0.25 -> 0.125) */\r
1417                 if( !bouncing )\r
1418                         VectorMA( gp->ambient[ i ], 0.125f, gp->directed[ i ], gp->ambient[ i ] );\r
1419                 \r
1420                 /* set minimum light and copy off to bytes */\r
1421                 VectorCopy( gp->ambient[ i ], color );\r
1422                 for( j = 0; j < 3; j++ )\r
1423                         if( color[ j ] < minGridLight[ j ] )\r
1424                                 color[ j ] = minGridLight[ j ];\r
1425                 ColorToBytes( color, bgp->ambient[ i ], 1.0f );\r
1426                 ColorToBytes( gp->directed[ i ], bgp->directed[ i ], 1.0f );\r
1427         }\r
1428         \r
1429         /* debug code */\r
1430         #if 0\r
1431                 //%     Sys_FPrintf( SYS_VRB, "%10d %10d %10d ", &gp->ambient[ 0 ][ 0 ], &gp->ambient[ 0 ][ 1 ], &gp->ambient[ 0 ][ 2 ] );\r
1432                 Sys_FPrintf( SYS_VRB, "%9d Amb: (%03.1f %03.1f %03.1f) Dir: (%03.1f %03.1f %03.1f)\n",\r
1433                         num,\r
1434                         gp->ambient[ 0 ][ 0 ], gp->ambient[ 0 ][ 1 ], gp->ambient[ 0 ][ 2 ],\r
1435                         gp->directed[ 0 ][ 0 ], gp->directed[ 0 ][ 1 ], gp->directed[ 0 ][ 2 ] );\r
1436         #endif\r
1437         \r
1438         /* store direction */\r
1439         NormalToLatLong( gp->dir, bgp->latLong );\r
1440 }\r
1441 \r
1442 \r
1443 \r
1444 /*\r
1445 SetupGrid()\r
1446 calculates the size of the lightgrid and allocates memory\r
1447 */\r
1448 \r
1449 void SetupGrid( void )\r
1450 {\r
1451         int                     i, j;\r
1452         vec3_t          maxs, oldGridSize;\r
1453         const char      *value;\r
1454         char            temp[ 64 ];\r
1455         \r
1456          \r
1457         /* don't do this if not grid lighting */\r
1458         if( noGridLighting )\r
1459                 return;\r
1460         \r
1461         /* ydnar: set grid size */\r
1462         value = ValueForKey( &entities[ 0 ], "gridsize" );\r
1463         if( value[ 0 ] != '\0' )\r
1464                 sscanf( value, "%f %f %f", &gridSize[ 0 ], &gridSize[ 1 ], &gridSize[ 2 ] );\r
1465         \r
1466         /* quantize it */\r
1467         VectorCopy( gridSize, oldGridSize );\r
1468         for( i = 0; i < 3; i++ )\r
1469                 gridSize[ i ] = gridSize[ i ] >= 8.0f ? floor( gridSize[ i ] ) : 8.0f;\r
1470         \r
1471         /* ydnar: increase gridSize until grid count is smaller than max allowed */\r
1472         numRawGridPoints = MAX_MAP_LIGHTGRID + 1;\r
1473         j = 0;\r
1474         while( numRawGridPoints > MAX_MAP_LIGHTGRID )\r
1475         {\r
1476                 /* get world bounds */\r
1477                 for( i = 0; i < 3; i++ )\r
1478                 {\r
1479                         gridMins[ i ] = gridSize[ i ] * ceil( bspModels[ 0 ].mins[ i ] / gridSize[ i ] );\r
1480                         maxs[ i ] = gridSize[ i ] * floor( bspModels[ 0 ].maxs[ i ] / gridSize[ i ] );\r
1481                         gridBounds[ i ] = (maxs[ i ] - gridMins[ i ]) / gridSize[ i ] + 1;\r
1482                 }\r
1483         \r
1484                 /* set grid size */\r
1485                 numRawGridPoints = gridBounds[ 0 ] * gridBounds[ 1 ] * gridBounds[ 2 ];\r
1486                 \r
1487                 /* increase grid size a bit */\r
1488                 if( numRawGridPoints > MAX_MAP_LIGHTGRID )\r
1489                         gridSize[ j++ % 3 ] += 16.0f;\r
1490         }\r
1491         \r
1492         /* print it */\r
1493         Sys_Printf( "Grid size = { %1.0f, %1.0f, %1.0f }\n", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );\r
1494         \r
1495         /* different? */\r
1496         if( !VectorCompare( gridSize, oldGridSize ) )\r
1497         {\r
1498                 sprintf( temp, "%.0f %.0f %.0f", gridSize[ 0 ], gridSize[ 1 ], gridSize[ 2 ] );\r
1499                 SetKeyValue( &entities[ 0 ], "gridsize", (const char*) temp );\r
1500                 Sys_FPrintf( SYS_VRB, "Storing adjusted grid size\n" );\r
1501         }\r
1502         \r
1503         /* 2nd variable. fixme: is this silly? */\r
1504         numBSPGridPoints = numRawGridPoints;\r
1505         \r
1506         /* allocate lightgrid */\r
1507         rawGridPoints = safe_malloc( numRawGridPoints * sizeof( *rawGridPoints ) );\r
1508         memset( rawGridPoints, 0, numRawGridPoints * sizeof( *rawGridPoints ) );\r
1509         \r
1510         if( bspGridPoints != NULL )\r
1511                 free( bspGridPoints );\r
1512         bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );\r
1513         memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );\r
1514         \r
1515         /* clear lightgrid */\r
1516         for( i = 0; i < numRawGridPoints; i++ )\r
1517         {\r
1518                 VectorCopy( ambientColor, rawGridPoints[ i ].ambient[ j ] );\r
1519                 rawGridPoints[ i ].styles[ 0 ] = LS_NORMAL;\r
1520                 bspGridPoints[ i ].styles[ 0 ] = LS_NORMAL;\r
1521                 for( j = 1; j < MAX_LIGHTMAPS; j++ )\r
1522                 {\r
1523                         rawGridPoints[ i ].styles[ j ] = LS_NONE;\r
1524                         bspGridPoints[ i ].styles[ j ] = LS_NONE;\r
1525                 }\r
1526         }\r
1527         \r
1528         /* note it */\r
1529         Sys_Printf( "%9d grid points\n", numRawGridPoints );\r
1530 }\r
1531 \r
1532 \r
1533 \r
1534 /*\r
1535 LightWorld()\r
1536 does what it says...\r
1537 */\r
1538 \r
1539 void LightWorld( void )\r
1540 {\r
1541         vec3_t          color;\r
1542         float           f;\r
1543         int                     b, bt;\r
1544         qboolean        minVertex, minGrid;\r
1545         const char      *value;\r
1546         \r
1547 \r
1548         /* ydnar: smooth normals */\r
1549         if( shade )\r
1550         {\r
1551                 Sys_Printf( "--- SmoothNormals ---\n" );\r
1552                 SmoothNormals();\r
1553         }\r
1554         \r
1555         /* determine the number of grid points */\r
1556         Sys_Printf( "--- SetupGrid ---\n" );\r
1557         SetupGrid();\r
1558         \r
1559         /* find the optional minimum lighting values */\r
1560         GetVectorForKey( &entities[ 0 ], "_color", color );\r
1561         if( VectorLength( color ) == 0.0f )\r
1562                 VectorSet( color, 1.0, 1.0, 1.0 );\r
1563         \r
1564         /* ambient */\r
1565         f = FloatForKey( &entities[ 0 ], "_ambient" );\r
1566         if( f == 0.0f )\r
1567                 f = FloatForKey( &entities[ 0 ], "ambient" );\r
1568         VectorScale( color, f, ambientColor );\r
1569         \r
1570         /* minvertexlight */\r
1571         minVertex = qfalse;\r
1572         value = ValueForKey( &entities[ 0 ], "_minvertexlight" );\r
1573         if( value[ 0 ] != '\0' )\r
1574         {\r
1575                 minVertex = qtrue;\r
1576                 f = atof( value );\r
1577                 VectorScale( color, f, minVertexLight );\r
1578         }\r
1579         \r
1580         /* mingridlight */\r
1581         minGrid = qfalse;\r
1582         value = ValueForKey( &entities[ 0 ], "_mingridlight" );\r
1583         if( value[ 0 ] != '\0' )\r
1584         {\r
1585                 minGrid = qtrue;\r
1586                 f = atof( value );\r
1587                 VectorScale( color, f, minGridLight );\r
1588         }\r
1589         \r
1590         /* minlight */\r
1591         value = ValueForKey( &entities[ 0 ], "_minlight" );\r
1592         if( value[ 0 ] != '\0' )\r
1593         {\r
1594                 f = atof( value );\r
1595                 VectorScale( color, f, minLight );\r
1596                 if( minVertex == qfalse )\r
1597                         VectorScale( color, f, minVertexLight );\r
1598                 if( minGrid == qfalse )\r
1599                         VectorScale( color, f, minGridLight );\r
1600         }\r
1601         \r
1602         /* create world lights */\r
1603         Sys_FPrintf( SYS_VRB, "--- CreateLights ---\n" );\r
1604         CreateEntityLights();\r
1605         CreateSurfaceLights();\r
1606         Sys_Printf( "%9d point lights\n", numPointLights );\r
1607         Sys_Printf( "%9d spotlights\n", numSpotLights );\r
1608         Sys_Printf( "%9d diffuse (area) lights\n", numDiffuseLights );\r
1609         Sys_Printf( "%9d sun/sky lights\n", numSunLights );\r
1610         \r
1611         /* calculate lightgrid */\r
1612         if( !noGridLighting )\r
1613         {\r
1614                 /* ydnar: set up light envelopes */\r
1615                 SetupEnvelopes( qtrue, fastgrid );\r
1616                 \r
1617                 Sys_Printf( "--- TraceGrid ---\n" );\r
1618                 RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );\r
1619                 Sys_Printf( "%d x %d x %d = %d grid\n",\r
1620                         gridBounds[ 0 ], gridBounds[ 1 ], gridBounds[ 2 ], numBSPGridPoints );\r
1621                 \r
1622                 /* ydnar: emit statistics on light culling */\r
1623                 Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );\r
1624                 Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );\r
1625         }\r
1626         \r
1627         /* slight optimization to remove a sqrt */\r
1628         subdivideThreshold *= subdivideThreshold;\r
1629         \r
1630         /* map the world luxels */\r
1631         Sys_Printf( "--- MapRawLightmap ---\n" );\r
1632         RunThreadsOnIndividual( numRawLightmaps, qtrue, MapRawLightmap );\r
1633         Sys_Printf( "%9d luxels\n", numLuxels );\r
1634         Sys_Printf( "%9d luxels mapped\n", numLuxelsMapped );\r
1635         Sys_Printf( "%9d luxels occluded\n", numLuxelsOccluded );\r
1636 \r
1637         /* ydnar: set up light envelopes */\r
1638         SetupEnvelopes( qfalse, fast );\r
1639         \r
1640         /* light up my world */\r
1641         lightsPlaneCulled = 0;\r
1642         lightsEnvelopeCulled = 0;\r
1643         lightsBoundsCulled = 0;\r
1644         lightsClusterCulled = 0;\r
1645         \r
1646         Sys_Printf( "--- IlluminateRawLightmap ---\n" );\r
1647         RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );\r
1648         Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );\r
1649         \r
1650         StitchSurfaceLightmaps();\r
1651         \r
1652         Sys_Printf( "--- IlluminateVertexes ---\n" );\r
1653         RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );\r
1654         Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );\r
1655         \r
1656         /* ydnar: emit statistics on light culling */\r
1657         Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );\r
1658         Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );\r
1659         Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );\r
1660         Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );\r
1661         \r
1662         /* radiosity */\r
1663         b = 1;\r
1664         bt = bounce;\r
1665         while( bounce > 0 )\r
1666         {\r
1667                 /* store off the bsp between bounces */\r
1668                 StoreSurfaceLightmaps();\r
1669                 Sys_Printf( "Writing %s\n", source );\r
1670                 WriteBSPFile( source );\r
1671                 \r
1672                 /* note it */\r
1673                 Sys_Printf( "\n--- Radiosity (bounce %d of %d) ---\n", b, bt );\r
1674                 \r
1675                 /* flag bouncing */\r
1676                 bouncing = qtrue;\r
1677                 VectorClear( ambientColor );\r
1678                 \r
1679                 /* generate diffuse lights */\r
1680                 RadFreeLights();\r
1681                 RadCreateDiffuseLights();\r
1682                 \r
1683                 /* setup light envelopes */\r
1684                 SetupEnvelopes( qfalse, fastbounce );\r
1685                 if( numLights == 0 )\r
1686                 {\r
1687                         Sys_Printf( "No diffuse light to calculate, ending radiosity.\n" );\r
1688                         break;\r
1689                 }\r
1690                 \r
1691                 /* add to lightgrid */\r
1692                 if( bouncegrid )\r
1693                 {\r
1694                         gridEnvelopeCulled = 0;\r
1695                         gridBoundsCulled = 0;\r
1696                         \r
1697                         Sys_Printf( "--- BounceGrid ---\n" );\r
1698                         RunThreadsOnIndividual( numRawGridPoints, qtrue, TraceGrid );\r
1699                         Sys_FPrintf( SYS_VRB, "%9d grid points envelope culled\n", gridEnvelopeCulled );\r
1700                         Sys_FPrintf( SYS_VRB, "%9d grid points bounds culled\n", gridBoundsCulled );\r
1701                 }\r
1702                 \r
1703                 /* light up my world */\r
1704                 lightsPlaneCulled = 0;\r
1705                 lightsEnvelopeCulled = 0;\r
1706                 lightsBoundsCulled = 0;\r
1707                 lightsClusterCulled = 0;\r
1708                 \r
1709                 Sys_Printf( "--- IlluminateRawLightmap ---\n" );\r
1710                 RunThreadsOnIndividual( numRawLightmaps, qtrue, IlluminateRawLightmap );\r
1711                 Sys_Printf( "%9d luxels illuminated\n", numLuxelsIlluminated );\r
1712                 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );\r
1713                 \r
1714                 StitchSurfaceLightmaps();\r
1715                 \r
1716                 Sys_Printf( "--- IlluminateVertexes ---\n" );\r
1717                 RunThreadsOnIndividual( numBSPDrawSurfaces, qtrue, IlluminateVertexes );\r
1718                 Sys_Printf( "%9d vertexes illuminated\n", numVertsIlluminated );\r
1719                 \r
1720                 /* ydnar: emit statistics on light culling */\r
1721                 Sys_FPrintf( SYS_VRB, "%9d lights plane culled\n", lightsPlaneCulled );\r
1722                 Sys_FPrintf( SYS_VRB, "%9d lights envelope culled\n", lightsEnvelopeCulled );\r
1723                 Sys_FPrintf( SYS_VRB, "%9d lights bounds culled\n", lightsBoundsCulled );\r
1724                 Sys_FPrintf( SYS_VRB, "%9d lights cluster culled\n", lightsClusterCulled );\r
1725                 \r
1726                 /* interate */\r
1727                 bounce--;\r
1728                 b++;\r
1729         }\r
1730 }\r
1731 \r
1732 \r
1733 \r
1734 /*\r
1735 LightMain()\r
1736 main routine for light processing\r
1737 */\r
1738 \r
1739 int LightMain( int argc, char **argv )\r
1740 {\r
1741         int                     i;\r
1742         float           f;\r
1743         char            mapSource[ 1024 ];\r
1744         const char      *value;\r
1745         \r
1746         \r
1747         /* note it */\r
1748         Sys_Printf( "--- Light ---\n" );\r
1749         \r
1750         /* process commandline arguments */\r
1751         for( i = 1; i < (argc - 1); i++ )\r
1752         {\r
1753                 /* lightsource scaling */\r
1754                 if( !strcmp( argv[ i ], "-point" ) || !strcmp( argv[ i ], "-pointscale" ) )\r
1755                 {\r
1756                         f = atof( argv[ i + 1 ] );\r
1757                         pointScale *= f;\r
1758                         Sys_Printf( "Point (entity) light scaled by %f to %f\n", f, pointScale );\r
1759                         i++;\r
1760                 }\r
1761                 \r
1762                 else if( !strcmp( argv[ i ], "-area" ) || !strcmp( argv[ i ], "-areascale" ) )\r
1763                 {\r
1764                         f = atof( argv[ i + 1 ] );\r
1765                         areaScale *= f;\r
1766                         Sys_Printf( "Area (shader) light scaled by %f to %f\n", f, areaScale );\r
1767                         i++;\r
1768                 }\r
1769                 \r
1770                 else if( !strcmp( argv[ i ], "-sky" ) || !strcmp( argv[ i ], "-skyscale" ) )\r
1771                 {\r
1772                         f = atof( argv[ i + 1 ] );\r
1773                         skyScale *= f;\r
1774                         Sys_Printf( "Sky/sun light scaled by %f to %f\n", f, skyScale );\r
1775                         i++;\r
1776                 }\r
1777                 \r
1778                 else if( !strcmp( argv[ i ], "-bouncescale" ) )\r
1779                 {\r
1780                         f = atof( argv[ i + 1 ] );\r
1781                         bounceScale *= f;\r
1782                         Sys_Printf( "Bounce (radiosity) light scaled by %f to %f\n", f, bounceScale );\r
1783                         i++;\r
1784                 }\r
1785                 \r
1786                 else if( !strcmp( argv[ i ], "-scale" ) )\r
1787                 {\r
1788                         f = atof( argv[ i + 1 ] );\r
1789                         pointScale *= f;\r
1790                         areaScale *= f;\r
1791                         skyScale *= f;\r
1792                         bounceScale *= f;\r
1793                         Sys_Printf( "All light scaled by %f\n", f );\r
1794                         i++;\r
1795                 }\r
1796                 \r
1797                 /* ydnar switches */\r
1798                 else if( !strcmp( argv[ i ], "-bounce" ) )\r
1799                 {\r
1800                         bounce = atoi( argv[ i + 1 ] );\r
1801                         if( bounce < 0 )\r
1802                                 bounce = 0;\r
1803                         else if( bounce > 0 )\r
1804                                 Sys_Printf( "Radiosity enabled with %d bounce(s)\n", bounce );\r
1805                         i++;\r
1806                 }\r
1807                 \r
1808                 else if( !strcmp( argv[ i ], "-supersample" ) || !strcmp( argv[ i ], "-super" ) )\r
1809                 {\r
1810                         superSample = atoi( argv[ i + 1 ] );\r
1811                         if( superSample < 1 )\r
1812                                 superSample = 1;\r
1813                         else if( superSample > 1 )\r
1814                                 Sys_Printf( "Ordered-grid supersampling enabled with %d sample(s) per lightmap texel\n", (superSample * superSample) );\r
1815                         i++;\r
1816                 }\r
1817                 \r
1818                 else if( !strcmp( argv[ i ], "-samples" ) )\r
1819                 {\r
1820                         lightSamples = atoi( argv[ i + 1 ] );\r
1821                         if( lightSamples < 1 )\r
1822                                 lightSamples = 1;\r
1823                         else if( lightSamples > 1 )\r
1824                                 Sys_Printf( "Adaptive supersampling enabled with %d sample(s) per lightmap texel\n", lightSamples );\r
1825                         i++;\r
1826                 }\r
1827                 \r
1828                 else if( !strcmp( argv[ i ], "-filter" ) )\r
1829                 {\r
1830                         filter = qtrue;\r
1831                         Sys_Printf( "Lightmap filtering enabled\n" );\r
1832                 }\r
1833                 \r
1834                 else if( !strcmp( argv[ i ], "-shadeangle" ) )\r
1835                 {\r
1836                         shadeAngleDegrees = atof( argv[ i + 1 ] );\r
1837                         if( shadeAngleDegrees < 0.0f )\r
1838                                 shadeAngleDegrees = 0.0f;\r
1839                         else if( shadeAngleDegrees > 0.0f )\r
1840                         {\r
1841                                 shade = qtrue;\r
1842                                 Sys_Printf( "Phong shading enabled with a breaking angle of %f degrees\n", shadeAngleDegrees );\r
1843                         }\r
1844                         i++;\r
1845                 }\r
1846                 \r
1847                 else if( !strcmp( argv[ i ], "-thresh" ) )\r
1848                 {\r
1849                         subdivideThreshold = atof( argv[ i + 1 ] );\r
1850                         if( subdivideThreshold < 0 )\r
1851                                 subdivideThreshold = DEFAULT_SUBDIVIDE_THRESHOLD;\r
1852                         else\r
1853                                 Sys_Printf( "Subdivision threshold set at %.3f\n", subdivideThreshold );\r
1854                         i++;\r
1855                 }\r
1856                 \r
1857                 else if( !strcmp( argv[ i ], "-approx" ) )\r
1858                 {\r
1859                         approximateTolerance = atoi( argv[ i + 1 ] );\r
1860                         if( approximateTolerance < 0 )\r
1861                                 approximateTolerance = 0;\r
1862                         else if( approximateTolerance > 0 )\r
1863                                 Sys_Printf( "Approximating lightmaps within a byte tolerance of %d\n", approximateTolerance );\r
1864                         i++;\r
1865                 }\r
1866                 \r
1867                 else if( !strcmp( argv[ i ], "-deluxe" ) || !strcmp( argv[ i ], "-deluxemap" ) )\r
1868                 {\r
1869                         deluxemap = qtrue;\r
1870                         Sys_Printf( "Generating deluxemaps for average light direction\n" );\r
1871                 }\r
1872                 \r
1873                 else if( !strcmp( argv[ i ], "-external" ) )\r
1874                 {\r
1875                         externalLightmaps = qtrue;\r
1876                         Sys_Printf( "Storing all lightmaps externally\n" );\r
1877                 }\r
1878 \r
1879                 else if( !strcmp( argv[ i ], "-lightmapsize" ) )\r
1880                 {\r
1881                         lmCustomSize = atoi( argv[ i + 1 ] );\r
1882                         \r
1883                         /* must be a power of 2 and greater than 2 */\r
1884                         if( ((lmCustomSize - 1) & lmCustomSize) || lmCustomSize < 2 )\r
1885                         {\r
1886                                 Sys_Printf( "WARNING: Lightmap size must be a power of 2, greater or equal to 2 pixels.\n" );\r
1887                                 lmCustomSize = LIGHTMAP_WIDTH;\r
1888                         }\r
1889                         i++;\r
1890                         Sys_Printf( "Default lightmap size set to %d x %d pixels\n", lmCustomSize, lmCustomSize );\r
1891                         \r
1892                         /* enable external lightmaps */\r
1893                         if( lmCustomSize != LIGHTMAP_WIDTH )\r
1894                         {\r
1895                                 externalLightmaps = qtrue;\r
1896                                 Sys_Printf( "Storing all lightmaps externally\n" );\r
1897                         }\r
1898                 }\r
1899                 \r
1900                 /* ydnar: add this to suppress warnings */\r
1901                 else if( !strcmp( argv[ i ],  "-custinfoparms") )\r
1902                 {\r
1903                         Sys_Printf( "Custom info parms enabled\n" );\r
1904                         useCustomInfoParms = qtrue;\r
1905                 }\r
1906                 \r
1907                 else if( !strcmp( argv[ i ], "-wolf" ) )\r
1908                 {\r
1909                         /* -game should already be set */\r
1910                         game->wolfLight = qtrue;\r
1911                         Sys_Printf( "Enabling Wolf lighting model\n" );\r
1912                 }\r
1913                 \r
1914                 else if( !strcmp( argv[ i ], "-sunonly" ) )\r
1915                 {\r
1916                         sunOnly = qtrue;\r
1917                         Sys_Printf( "Only computing sunlight\n" );\r
1918                 }\r
1919                 \r
1920                 else if( !strcmp( argv[ i ], "-bounceonly" ) )\r
1921                 {\r
1922                         bounceOnly = qtrue;\r
1923                         Sys_Printf( "Storing bounced light (radiosity) only\n" );\r
1924                 }\r
1925                 \r
1926                 else if( !strcmp( argv[ i ], "-nocollapse" ) )\r
1927                 {\r
1928                         noCollapse = qtrue;\r
1929                         Sys_Printf( "Identical lightmap collapsing disabled\n" );\r
1930                 }\r
1931                 \r
1932                 else if( !strcmp( argv[ i ], "-shade" ) )\r
1933                 {\r
1934                         shade = qtrue;\r
1935                         Sys_Printf( "Phong shading enabled\n" );\r
1936                 }\r
1937                 \r
1938                 else if( !strcmp( argv[ i ], "-bouncegrid") )\r
1939                 {\r
1940                         bouncegrid = qtrue;\r
1941                         if( bounce > 0 )\r
1942                                 Sys_Printf( "Grid lighting with radiosity enabled\n" );\r
1943                 }\r
1944                 \r
1945                 else if( !strcmp( argv[ i ], "-smooth" ) )\r
1946                 {\r
1947                         smooth = qtrue;\r
1948                         lightSamples = EXTRA_SCALE;\r
1949                         Sys_Printf( "The -smooth argument is deprecated, use \"-samples 2\" instead\n" );\r
1950                 }\r
1951                 \r
1952                 else if( !strcmp( argv[ i ], "-fast" ) )\r
1953                 {\r
1954                         fast = qtrue;\r
1955                         fastgrid = qtrue;\r
1956                         fastbounce = qtrue;\r
1957                         Sys_Printf( "Fast mode enabled\n" );\r
1958                 }\r
1959                 \r
1960                 else if( !strcmp( argv[ i ], "-faster" ) )\r
1961                 {\r
1962                         faster = qtrue;\r
1963                         fast = qtrue;\r
1964                         fastgrid = qtrue;\r
1965                         fastbounce = qtrue;\r
1966                         Sys_Printf( "Faster mode enabled\n" );\r
1967                 }\r
1968                 \r
1969                 else if( !strcmp( argv[ i ], "-fastgrid" ) )\r
1970                 {\r
1971                         fastgrid = qtrue;\r
1972                         Sys_Printf( "Fast grid lighting enabled\n" );\r
1973                 }\r
1974                 \r
1975                 else if( !strcmp( argv[ i ], "-fastbounce" ) )\r
1976                 {\r
1977                         fastbounce = qtrue;\r
1978                         Sys_Printf( "Fast bounce mode enabled\n" );\r
1979                 }\r
1980                 \r
1981                 else if( !strcmp( argv[ i ], "-cheap" ) )\r
1982                 {\r
1983                         cheap = qtrue;\r
1984                         cheapgrid = qtrue;\r
1985                         Sys_Printf( "Cheap mode enabled\n" );\r
1986                 }\r
1987 \r
1988                 else if( !strcmp( argv[ i ], "-cheapgrid" ) )\r
1989                 {\r
1990                         cheapgrid = qtrue;\r
1991                         Sys_Printf( "Cheap grid mode enabled\n" );\r
1992                 }\r
1993                 \r
1994                 else if( !strcmp( argv[ i ], "-normalmap" ) )\r
1995                 {\r
1996                         normalmap = qtrue;\r
1997                         Sys_Printf( "Storing normal map instead of lightmap\n" );\r
1998                 }\r
1999                 \r
2000                 else if( !strcmp( argv[ i ], "-trisoup" ) )\r
2001                 {\r
2002                         trisoup = qtrue;\r
2003                         Sys_Printf( "Converting brush faces to triangle soup\n" );\r
2004                 }\r
2005                 \r
2006                 else if( !strcmp( argv[ i ], "-debug" ) )\r
2007                 {\r
2008                         debug = qtrue;\r
2009                         Sys_Printf( "Lightmap debugging enabled\n" );\r
2010                 }\r
2011                 \r
2012                 else if( !strcmp( argv[ i ], "-debugsurfaces" ) || !strcmp( argv[ i ], "-debugsurface" ) )\r
2013                 {\r
2014                         debugSurfaces = qtrue;\r
2015                         Sys_Printf( "Lightmap surface debugging enabled\n" );\r
2016                 }\r
2017                 \r
2018                 else if( !strcmp( argv[ i ], "-debugunused" ) )\r
2019                 {\r
2020                         debugUnused = qtrue;\r
2021                         Sys_Printf( "Unused luxel debugging enabled\n" );\r
2022                 }\r
2023 \r
2024                 else if( !strcmp( argv[ i ], "-debugaxis" ) )\r
2025                 {\r
2026                         debugAxis = qtrue;\r
2027                         Sys_Printf( "Lightmap axis debugging enabled\n" );\r
2028                 }\r
2029                 \r
2030                 else if( !strcmp( argv[ i ], "-debugcluster" ) )\r
2031                 {\r
2032                         debugCluster = qtrue;\r
2033                         Sys_Printf( "Luxel cluster debugging enabled\n" );\r
2034                 }\r
2035                 \r
2036                 else if( !strcmp( argv[ i ], "-debugorigin" ) )\r
2037                 {\r
2038                         debugOrigin = qtrue;\r
2039                         Sys_Printf( "Luxel origin debugging enabled\n" );\r
2040                 }\r
2041                 \r
2042                 else if( !strcmp( argv[ i ], "-debugdeluxe" ) )\r
2043                 {\r
2044                         deluxemap = qtrue;\r
2045                         debugDeluxemap = qtrue;\r
2046                         Sys_Printf( "Deluxemap debugging enabled\n" );\r
2047                 }\r
2048                 \r
2049                 else if( !strcmp( argv[ i ], "-export" ) )\r
2050                 {\r
2051                         exportLightmaps = qtrue;\r
2052                         Sys_Printf( "Exporting lightmaps\n" );\r
2053                 }\r
2054                 \r
2055                 else if( !strcmp(argv[ i ], "-notrace" )) \r
2056                 {\r
2057                         noTrace = qtrue;\r
2058                         Sys_Printf( "Shadow occlusion disabled\n" );\r
2059                 }\r
2060                 else if( !strcmp(argv[ i ], "-patchshadows" ) )\r
2061                 {\r
2062                         patchShadows = qtrue;\r
2063                         Sys_Printf( "Patch shadow casting enabled\n" );\r
2064                 }\r
2065                 else if( !strcmp( argv[ i ], "-extra" ) )\r
2066                 {\r
2067                         extra = qtrue;\r
2068                         superSample = EXTRA_SCALE;              /* ydnar */\r
2069                         Sys_Printf( "The -extra argument is deprecated, use \"-super 2\" instead\n" );\r
2070                 }\r
2071                 else if( !strcmp( argv[ i ], "-extrawide" ) )\r
2072                 {\r
2073                         extra = qtrue;\r
2074                         extraWide = qtrue;\r
2075                         superSample = EXTRAWIDE_SCALE;  /* ydnar */\r
2076                         filter = qtrue;                                 /* ydnar */\r
2077                         Sys_Printf( "The -extrawide argument is deprecated, use \"-filter [-super 2]\" instead\n");\r
2078                 }\r
2079                 else if( !strcmp( argv[ i ], "-samplesize" ) )\r
2080                 {\r
2081                         sampleSize = atoi( argv[ i + 1 ] );\r
2082                         if( sampleSize < 1 )\r
2083                                 sampleSize = 1;\r
2084                         i++;\r
2085                         Sys_Printf( "Default lightmap sample size set to %dx%d units\n", sampleSize, sampleSize );\r
2086                 }\r
2087                 else if( !strcmp( argv[ i ], "-novertex" ) )\r
2088                 {\r
2089                         noVertexLighting = qtrue;\r
2090                         Sys_Printf( "Disabling vertex lighting\n" );\r
2091                 }\r
2092                 else if( !strcmp( argv[ i ], "-nogrid" ) )\r
2093                 {\r
2094                         noGridLighting = qtrue;\r
2095                         Sys_Printf( "Disabling grid lighting\n" );\r
2096                 }\r
2097                 else if( !strcmp( argv[ i ], "-border" ) )\r
2098                 {\r
2099                         lightmapBorder = qtrue;\r
2100                         Sys_Printf( "Adding debug border to lightmaps\n" );\r
2101                 }\r
2102                 else if( !strcmp( argv[ i ], "-nosurf" ) )\r
2103                 {\r
2104                         noSurfaces = qtrue;\r
2105                         Sys_Printf( "Not tracing against surfaces\n" );\r
2106                 }\r
2107                 else if( !strcmp( argv[ i ], "-dump" ) )\r
2108                 {\r
2109                         dump = qtrue;\r
2110                         Sys_Printf( "Dumping radiosity lights into numbered prefabs\n" );\r
2111                 }\r
2112                 else if( !strcmp( argv[ i ], "-lomem" ) )\r
2113                 {\r
2114                         loMem = qtrue;\r
2115                         Sys_Printf( "Enabling low-memory (potentially slower) lighting mode\n" );\r
2116                 }\r
2117                 \r
2118                 else\r
2119                         Sys_Printf( "WARNING: Unknown option \"%s\"\n", argv[ i ] );\r
2120         }\r
2121         \r
2122         /* clean up map name */\r
2123         strcpy( source, ExpandArg( argv[ i ] ) );\r
2124         StripExtension( source );\r
2125         DefaultExtension( source, ".bsp" );\r
2126         strcpy( mapSource, ExpandArg( argv[ i ] ) );\r
2127         StripExtension( mapSource );\r
2128         DefaultExtension( mapSource, ".map" );\r
2129         \r
2130         /* ydnar: set default sample size */\r
2131         SetDefaultSampleSize( sampleSize );\r
2132         \r
2133         /* ydnar: handle shaders */\r
2134         BeginMapShaderFile( source );\r
2135         LoadShaderInfo();\r
2136         \r
2137         /* note loading */\r
2138         Sys_Printf( "Loading %s\n", source );\r
2139         \r
2140         /* ydnar: load surface file */\r
2141         LoadSurfaceExtraFile( source );\r
2142         \r
2143         /* load bsp file */\r
2144         LoadBSPFile( source );\r
2145         \r
2146         /* parse bsp entities */\r
2147         ParseEntities();\r
2148         \r
2149         /* load map file */\r
2150         value = ValueForKey( &entities[ 0 ], "_keepLights" );\r
2151         if( value[ 0 ] != '1' )\r
2152                 LoadMapFile( mapSource, qtrue );\r
2153         \r
2154         /* set the entity/model origins and init yDrawVerts */\r
2155         SetEntityOrigins();\r
2156         \r
2157         /* ydnar: set up optimization */\r
2158         SetupBrushes();\r
2159         SetupSurfaceLightmaps();\r
2160         \r
2161         /* initialize the surface facet tracing */\r
2162         SetupTraceNodes();\r
2163         \r
2164         /* light the world */\r
2165         LightWorld();\r
2166         \r
2167         /* ydnar: store off lightmaps */\r
2168         StoreSurfaceLightmaps();\r
2169         \r
2170         /* write out the bsp */\r
2171         UnparseEntities();\r
2172         Sys_Printf( "Writing %s\n", source );\r
2173         WriteBSPFile( source );\r
2174         \r
2175         /* ydnar: export lightmaps */\r
2176         if( exportLightmaps && !externalLightmaps )\r
2177                 ExportLightmaps();\r
2178         \r
2179         /* return to sender */\r
2180         return 0;\r
2181 }\r
2182 \r