Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

light.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // light.c
00023 
00024 #include "light.h"
00025 #ifdef _WIN32
00026 #ifdef _TTIMOBUILD
00027 #include "pakstuff.h"
00028 #else
00029 #include "../libs/pakstuff.h"
00030 #endif
00031 #endif
00032 
00033 
00034 #define EXTRASCALE  2
00035 
00036 typedef struct {
00037     float       plane[4];
00038     vec3_t      origin;
00039     vec3_t      vectors[2];
00040     shaderInfo_t    *si;
00041 } filter_t;
00042 
00043 #define MAX_FILTERS 1024
00044 filter_t    filters[MAX_FILTERS];
00045 int         numFilters;
00046 
00047 extern char source[1024];
00048 
00049 qboolean    notrace;
00050 qboolean    patchshadows;
00051 qboolean    dump;
00052 qboolean    extra;
00053 qboolean    extraWide;
00054 qboolean    lightmapBorder;
00055 
00056 qboolean    noSurfaces;
00057 
00058 int         samplesize = 16;        //sample size in units
00059 int         novertexlighting = 0;
00060 int         nogridlighting = 0;
00061 
00062 // for run time tweaking of all area sources in the level
00063 float       areaScale = 0.25;
00064 
00065 // for run time tweaking of all point sources in the level
00066 float       pointScale = 7500;
00067 
00068 qboolean    exactPointToPolygon = qtrue;
00069 
00070 float       formFactorValueScale = 3;
00071 
00072 float       linearScale = 1.0 / 8000;
00073 
00074 light_t     *lights;
00075 int         numPointLights;
00076 int         numAreaLights;
00077 
00078 FILE        *dumpFile;
00079 
00080 int         c_visible, c_occluded;
00081 
00082 //int           defaultLightSubdivide = 128;        // vary by surface size?
00083 int         defaultLightSubdivide = 999;        // vary by surface size?
00084 
00085 vec3_t      ambientColor;
00086 
00087 vec3_t      surfaceOrigin[ MAX_MAP_DRAW_SURFS ];
00088 int         entitySurface[ MAX_MAP_DRAW_SURFS ];
00089 
00090 // 7,9,11 normalized to avoid being nearly coplanar with common faces
00091 //vec3_t        sunDirection = { 0.441835, 0.56807, 0.694313 };
00092 //vec3_t        sunDirection = { 0.45, 0, 0.9 };
00093 //vec3_t        sunDirection = { 0, 0, 1 };
00094 
00095 // these are usually overrided by shader values
00096 vec3_t      sunDirection = { 0.45, 0.3, 0.9 };
00097 vec3_t      sunLight = { 100, 100, 50 };
00098 
00099 
00100 
00101 typedef struct {
00102     dbrush_t    *b;
00103     vec3_t      bounds[2];
00104 } skyBrush_t;
00105 
00106 int         numSkyBrushes;
00107 skyBrush_t  skyBrushes[MAX_MAP_BRUSHES];
00108 
00109 
00110 /*
00111 
00112 the corners of a patch mesh will always be exactly at lightmap samples.
00113 The dimensions of the lightmap will be equal to the average length of the control
00114 mesh in each dimension divided by 2.
00115 The lightmap sample points should correspond to the chosen subdivision points.
00116 
00117 */
00118 
00119 /*
00120 ===============================================================
00121 
00122 SURFACE LOADING
00123 
00124 ===============================================================
00125 */
00126 
00127 #define MAX_FACE_POINTS     128
00128 
00129 /*
00130 ===============
00131 SubdivideAreaLight
00132 
00133 Subdivide area lights that are very large
00134 A light that is subdivided will never backsplash, avoiding weird pools of light near edges
00135 ===============
00136 */
00137 void SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal, 
00138                         float areaSubdivide, qboolean backsplash ) {
00139     float           area, value, intensity;
00140     light_t         *dl, *dl2;
00141     vec3_t          mins, maxs;
00142     int             axis;
00143     winding_t       *front, *back;
00144     vec3_t          planeNormal;
00145     float           planeDist;
00146 
00147     if ( !w ) {
00148         return;
00149     }
00150 
00151     WindingBounds( w, mins, maxs );
00152 
00153     // check for subdivision
00154     for ( axis = 0 ; axis < 3 ; axis++ ) {
00155         if ( maxs[axis] - mins[axis] > areaSubdivide ) {
00156             VectorClear( planeNormal );
00157             planeNormal[axis] = 1;
00158             planeDist = ( maxs[axis] + mins[axis] ) * 0.5;
00159             ClipWindingEpsilon ( w, planeNormal, planeDist, ON_EPSILON, &front, &back );
00160             SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse );
00161             SubdivideAreaLight( ls, back, normal, areaSubdivide, qfalse );
00162             FreeWinding( w );
00163             return;
00164         }
00165     }
00166 
00167     // create a light from this
00168     area = WindingArea (w);
00169     if ( area <= 0 || area > 20000000 ) {
00170         return;
00171     }
00172 
00173     numAreaLights++;
00174     dl = malloc(sizeof(*dl));
00175     memset (dl, 0, sizeof(*dl));
00176     dl->next = lights;
00177     lights = dl;
00178     dl->type = emit_area;
00179 
00180     WindingCenter( w, dl->origin );
00181     dl->w = w;
00182     VectorCopy ( normal, dl->normal);
00183     dl->dist = DotProduct( dl->origin, normal );
00184 
00185     value = ls->value;
00186     intensity = value * area * areaScale;
00187     VectorAdd( dl->origin, dl->normal, dl->origin );
00188 
00189     VectorCopy( ls->color, dl->color );
00190 
00191     dl->photons = intensity;
00192 
00193     // emitColor is irrespective of the area
00194     VectorScale( ls->color, value*formFactorValueScale*areaScale, dl->emitColor );
00195 
00196     dl->si = ls;
00197 
00198     if ( ls->contents & CONTENTS_FOG ) {
00199         dl->twosided = qtrue;
00200     }
00201 
00202     // optionally create a point backsplash light
00203     if ( backsplash && ls->backsplashFraction > 0 ) {
00204         dl2 = malloc(sizeof(*dl));
00205         memset (dl2, 0, sizeof(*dl2));
00206         dl2->next = lights;
00207         lights = dl2;
00208         dl2->type = emit_point;
00209 
00210         VectorMA( dl->origin, ls->backsplashDistance, normal, dl2->origin );
00211 
00212         VectorCopy( ls->color, dl2->color );
00213 
00214         dl2->photons = dl->photons * ls->backsplashFraction;
00215         dl2->si = ls;
00216     }
00217 }
00218 
00219 
00220 /*
00221 ===============
00222 CountLightmaps
00223 ===============
00224 */
00225 void CountLightmaps( void ) {
00226     int         count;
00227     int         i;
00228     dsurface_t  *ds;
00229 
00230     qprintf ("--- CountLightmaps ---\n");
00231     count = 0;
00232     for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
00233         // see if this surface is light emiting
00234         ds = &drawSurfaces[i];
00235         if ( ds->lightmapNum > count ) {
00236             count = ds->lightmapNum;
00237         }
00238     }
00239 
00240     count++;
00241     numLightBytes = count * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3;
00242     if ( numLightBytes > MAX_MAP_LIGHTING ) {
00243         Error("MAX_MAP_LIGHTING exceeded");
00244     }
00245 
00246     qprintf( "%5i drawSurfaces\n", numDrawSurfaces );
00247     qprintf( "%5i lightmaps\n", count );
00248 }
00249 
00250 /*
00251 ===============
00252 CreateSurfaceLights
00253 
00254 This creates area lights
00255 ===============
00256 */
00257 void CreateSurfaceLights( void ) {
00258     int             i, j, side;
00259     dsurface_t      *ds;
00260     shaderInfo_t    *ls;
00261     winding_t       *w;
00262     cFacet_t        *f;
00263     light_t         *dl;
00264     vec3_t          origin;
00265     drawVert_t      *dv;
00266     int             c_lightSurfaces;
00267     float           lightSubdivide;
00268     vec3_t          normal;
00269 
00270     qprintf ("--- CreateSurfaceLights ---\n");
00271     c_lightSurfaces = 0;
00272 
00273     for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
00274         // see if this surface is light emiting
00275         ds = &drawSurfaces[i];
00276 
00277         ls = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
00278         if ( ls->value == 0 ) {
00279             continue;
00280         }
00281 
00282         // determine how much we need to chop up the surface
00283         if ( ls->lightSubdivide ) {
00284             lightSubdivide = ls->lightSubdivide;
00285         } else {
00286             lightSubdivide = defaultLightSubdivide;
00287         }
00288 
00289         c_lightSurfaces++;
00290 
00291         // an autosprite shader will become
00292         // a point light instead of an area light
00293         if ( ls->autosprite ) {
00294             // autosprite geometry should only have four vertexes
00295             if ( surfaceTest[i] ) {
00296                 // curve or misc_model
00297                 f = surfaceTest[i]->facets;
00298                 if ( surfaceTest[i]->numFacets != 1 || f->numBoundaries != 4 ) {
00299                     _printf( "WARNING: surface at (%i %i %i) has autosprite shader but isn't a quad\n",
00300                         (int)f->points[0], (int)f->points[1], (int)f->points[2] );
00301                 }
00302                 VectorAdd( f->points[0], f->points[1], origin );
00303                 VectorAdd( f->points[2], origin, origin );
00304                 VectorAdd( f->points[3], origin, origin );
00305                 VectorScale( origin, 0.25, origin );
00306             } else {
00307                 // normal polygon
00308                 dv = &drawVerts[ ds->firstVert ];
00309                 if ( ds->numVerts != 4 ) {
00310                     _printf( "WARNING: surface at (%i %i %i) has autosprite shader but %i verts\n",
00311                         (int)dv->xyz[0], (int)dv->xyz[1], (int)dv->xyz[2] );
00312                     continue;
00313                 }
00314 
00315                 VectorAdd( dv[0].xyz, dv[1].xyz, origin );
00316                 VectorAdd( dv[2].xyz, origin, origin );
00317                 VectorAdd( dv[3].xyz, origin, origin );
00318                 VectorScale( origin, 0.25, origin );
00319             }
00320 
00321 
00322             numPointLights++;
00323             dl = malloc(sizeof(*dl));
00324             memset (dl, 0, sizeof(*dl));
00325             dl->next = lights;
00326             lights = dl;
00327 
00328             VectorCopy( origin, dl->origin );
00329             VectorCopy( ls->color, dl->color );
00330             dl->photons = ls->value * pointScale;
00331             dl->type = emit_point;
00332             continue;
00333         }
00334 
00335         // possibly create for both sides of the polygon
00336         for ( side = 0 ; side <= ls->twoSided ; side++ ) {
00337             // create area lights
00338             if ( surfaceTest[i] ) {
00339                 // curve or misc_model
00340                 for ( j = 0 ; j < surfaceTest[i]->numFacets ; j++ ) {
00341                     f = surfaceTest[i]->facets + j;
00342                     w = AllocWinding( f->numBoundaries );
00343                     w->numpoints = f->numBoundaries;
00344                     memcpy( w->p, f->points, f->numBoundaries * 12 );
00345 
00346                     VectorCopy( f->surface, normal );
00347                     if ( side ) {
00348                         winding_t   *t;
00349 
00350                         t = w;
00351                         w = ReverseWinding( t );
00352                         FreeWinding( t );
00353                         VectorSubtract( vec3_origin, normal, normal );
00354                     }
00355                     SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
00356                 }
00357             } else {
00358                 // normal polygon
00359 
00360                 w = AllocWinding( ds->numVerts );
00361                 w->numpoints = ds->numVerts;
00362                 for ( j = 0 ; j < ds->numVerts ; j++ ) {
00363                     VectorCopy( drawVerts[ds->firstVert+j].xyz, w->p[j] );
00364                 }
00365                 VectorCopy( ds->lightmapVecs[2], normal );
00366                 if ( side ) {
00367                     winding_t   *t;
00368 
00369                     t = w;
00370                     w = ReverseWinding( t );
00371                     FreeWinding( t );
00372                     VectorSubtract( vec3_origin, normal, normal );
00373                 }
00374                 SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
00375             }
00376         }
00377     }
00378 
00379     _printf( "%5i light emitting surfaces\n", c_lightSurfaces );
00380 }
00381 
00382 
00383 
00384 /*
00385 ================
00386 FindSkyBrushes
00387 ================
00388 */
00389 void FindSkyBrushes( void ) {
00390     int             i, j;
00391     dbrush_t        *b;
00392     skyBrush_t      *sb;
00393     shaderInfo_t    *si;
00394     dbrushside_t    *s;
00395 
00396     // find the brushes
00397     for ( i = 0 ; i < numbrushes ; i++ ) {
00398         b = &dbrushes[i];
00399         for ( j = 0 ; j < b->numSides ; j++ ) {
00400             s = &dbrushsides[ b->firstSide + j ];
00401             if ( dshaders[ s->shaderNum ].surfaceFlags & SURF_SKY ) {
00402                 sb = &skyBrushes[ numSkyBrushes ];
00403                 sb->b = b;
00404                 sb->bounds[0][0] = -dplanes[ dbrushsides[ b->firstSide + 0 ].planeNum ].dist - 1;
00405                 sb->bounds[1][0] = dplanes[ dbrushsides[ b->firstSide + 1 ].planeNum ].dist + 1;
00406                 sb->bounds[0][1] = -dplanes[ dbrushsides[ b->firstSide + 2 ].planeNum ].dist - 1;
00407                 sb->bounds[1][1] = dplanes[ dbrushsides[ b->firstSide + 3 ].planeNum ].dist + 1;
00408                 sb->bounds[0][2] = -dplanes[ dbrushsides[ b->firstSide + 4 ].planeNum ].dist - 1;
00409                 sb->bounds[1][2] = dplanes[ dbrushsides[ b->firstSide + 5 ].planeNum ].dist + 1;
00410                 numSkyBrushes++;
00411                 break;
00412             }
00413         }
00414     }
00415 
00416     // default
00417     VectorNormalize( sunDirection, sunDirection );
00418 
00419     // find the sky shader
00420     for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
00421         si = ShaderInfoForShader( dshaders[ drawSurfaces[i].shaderNum ].shader );
00422         if ( si->surfaceFlags & SURF_SKY ) {
00423             VectorCopy( si->sunLight, sunLight );
00424             VectorCopy( si->sunDirection, sunDirection );
00425             break;
00426         }
00427     }
00428 }
00429 
00430 /*
00431 =================================================================
00432 
00433   LIGHT SETUP
00434 
00435 =================================================================
00436 */
00437 
00438 /*
00439 ==================
00440 FindTargetEntity
00441 ==================
00442 */
00443 entity_t *FindTargetEntity( const char *target ) {
00444     int         i;
00445     const char  *n;
00446 
00447     for ( i = 0 ; i < num_entities ; i++ ) {
00448         n = ValueForKey (&entities[i], "targetname");
00449         if ( !strcmp (n, target) ) {
00450             return &entities[i];
00451         }
00452     }
00453 
00454     return NULL;
00455 }
00456 
00457 
00458 
00459 /*
00460 =============
00461 CreateEntityLights
00462 =============
00463 */
00464 void CreateEntityLights (void)
00465 {
00466     int     i;
00467     light_t *dl;
00468     entity_t    *e, *e2;
00469     const char  *name;
00470     const char  *target;
00471     vec3_t  dest;
00472     const char  *_color;
00473     float   intensity;
00474     int     spawnflags;
00475 
00476     //
00477     // entities
00478     //
00479     for ( i = 0 ; i < num_entities ; i++ ) {
00480         e = &entities[i];
00481         name = ValueForKey (e, "classname");
00482         if (strncmp (name, "light", 5))
00483             continue;
00484 
00485         numPointLights++;
00486         dl = malloc(sizeof(*dl));
00487         memset (dl, 0, sizeof(*dl));
00488         dl->next = lights;
00489         lights = dl;
00490 
00491         spawnflags = FloatForKey (e, "spawnflags");
00492         if ( spawnflags & 1 ) {
00493             dl->linearLight = qtrue;
00494         }
00495 
00496         GetVectorForKey (e, "origin", dl->origin);
00497         dl->style = FloatForKey (e, "_style");
00498         if (!dl->style)
00499             dl->style = FloatForKey (e, "style");
00500         if (dl->style < 0)
00501             dl->style = 0;
00502 
00503         intensity = FloatForKey (e, "light");
00504         if (!intensity)
00505             intensity = FloatForKey (e, "_light");
00506         if (!intensity)
00507             intensity = 300;
00508         _color = ValueForKey (e, "_color");
00509         if (_color && _color[0])
00510         {
00511             sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]);
00512             ColorNormalize (dl->color, dl->color);
00513         }
00514         else
00515             dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
00516 
00517         intensity = intensity * pointScale;
00518         dl->photons = intensity;
00519 
00520         dl->type = emit_point;
00521 
00522         // lights with a target will be spotlights
00523         target = ValueForKey (e, "target");
00524 
00525         if ( target[0] ) {
00526             float   radius;
00527             float   dist;
00528 
00529             e2 = FindTargetEntity (target);
00530             if (!e2) {
00531                 _printf ("WARNING: light at (%i %i %i) has missing target\n",
00532                 (int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
00533             } else {
00534                 GetVectorForKey (e2, "origin", dest);
00535                 VectorSubtract (dest, dl->origin, dl->normal);
00536                 dist = VectorNormalize (dl->normal, dl->normal);
00537                 radius = FloatForKey (e, "radius");
00538                 if ( !radius ) {
00539                     radius = 64;
00540                 }
00541                 if ( !dist ) {
00542                     dist = 64;
00543                 }
00544                 dl->radiusByDist = (radius + 16) / dist;
00545                 dl->type = emit_spotlight;
00546             }
00547         }
00548     }
00549 }
00550 
00551 //=================================================================
00552 
00553 /*
00554 ================
00555 SetEntityOrigins
00556 
00557 Find the offset values for inline models
00558 ================
00559 */
00560 void SetEntityOrigins( void ) {
00561     int         i, j;
00562     entity_t    *e;
00563     vec3_t      origin;
00564     const char  *key;
00565     int         modelnum;
00566     dmodel_t    *dm;
00567 
00568     for ( i=0 ; i < num_entities ; i++ ) {
00569         e = &entities[i];
00570         key = ValueForKey (e, "model");
00571         if ( key[0] != '*' ) {
00572             continue;
00573         }
00574         modelnum = atoi( key + 1 );
00575         dm = &dmodels[ modelnum ];
00576 
00577         // set entity surface to true for all surfaces for this model
00578         for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
00579             entitySurface[ dm->firstSurface + j ] = qtrue;
00580         }
00581 
00582         key = ValueForKey (e, "origin");
00583         if ( !key[0] ) {
00584             continue;
00585         }
00586         GetVectorForKey ( e, "origin", origin );
00587 
00588         // set origin for all surfaces for this model
00589         for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
00590             VectorCopy( origin, surfaceOrigin[ dm->firstSurface + j ] );
00591         }
00592     }
00593 }
00594 
00595 
00596 /*
00597 =================================================================
00598 
00599 
00600 =================================================================
00601 */
00602 
00603 #define MAX_POINTS_ON_WINDINGS  64
00604 
00605 /*
00606 ================
00607 PointToPolygonFormFactor
00608 ================
00609 */
00610 float   PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w ) {
00611     vec3_t      triVector, triNormal;
00612     int         i, j;
00613     vec3_t      dirs[MAX_POINTS_ON_WINDING];
00614     float       total;
00615     float       dot, angle, facing;
00616 
00617     for ( i = 0 ; i < w->numpoints ; i++ ) {
00618         VectorSubtract( w->p[i], point, dirs[i] );
00619         VectorNormalize( dirs[i], dirs[i] );
00620     }
00621 
00622     // duplicate first vertex to avoid mod operation
00623     VectorCopy( dirs[0], dirs[i] );
00624 
00625     total = 0;
00626     for ( i = 0 ; i < w->numpoints ; i++ ) {
00627         j = i+1;
00628         dot = DotProduct( dirs[i], dirs[j] );
00629 
00630         // roundoff can cause slight creep, which gives an IND from acos
00631         if ( dot > 1.0 ) {
00632             dot = 1.0;
00633         } else if ( dot < -1.0 ) {
00634             dot = -1.0;
00635         }
00636         
00637         angle = acos( dot );
00638         CrossProduct( dirs[i], dirs[j], triVector );
00639         if ( VectorNormalize( triVector, triNormal ) < 0.0001 ) {
00640             continue;
00641         }
00642         facing = DotProduct( normal, triNormal );
00643         total += facing * angle;
00644 
00645         if ( total > 6.3 || total < -6.3 ) {
00646             static qboolean printed;
00647 
00648             if ( !printed ) {
00649                 printed = qtrue;
00650                 _printf( "WARNING: bad PointToPolygonFormFactor: %f at %1.1f %1.1f %1.1f from %1.1f %1.1f %1.1f\n", total,
00651                     w->p[i][0], w->p[i][1], w->p[i][2], point[0], point[1], point[2]);
00652             }
00653             return 0;
00654         }
00655 
00656     }
00657 
00658     total /= 2*3.141592657;     // now in the range of 0 to 1 over the entire incoming hemisphere
00659 
00660     return total;
00661 }
00662 
00663 
00664 /*
00665 ================
00666 FilterTrace
00667 
00668 Returns 0 to 1.0 filter fractions for the given trace
00669 ================
00670 */
00671 void    FilterTrace( const vec3_t start, const vec3_t end, vec3_t filter ) {
00672     float       d1, d2;
00673     filter_t    *f;
00674     int         filterNum;
00675     vec3_t      point;
00676     float       frac;
00677     int         i;
00678     float       s, t;
00679     int         u, v;
00680     int         x, y;
00681     byte        *pixel;
00682     float       radius;
00683     float       len;
00684     vec3_t      total;
00685 
00686     filter[0] = 1.0;
00687     filter[1] = 1.0;
00688     filter[2] = 1.0;
00689 
00690     for ( filterNum = 0 ; filterNum < numFilters ; filterNum++ ) {
00691         f = &filters[ filterNum ];
00692 
00693         // see if the plane is crossed
00694         d1 = DotProduct( start, f->plane ) - f->plane[3];
00695         d2 = DotProduct( end, f->plane ) - f->plane[3];
00696 
00697         if ( ( d1 < 0 ) == ( d2 < 0 ) ) {
00698             continue;
00699         }
00700 
00701         // calculate the crossing point
00702         frac = d1 / ( d1 - d2 );
00703 
00704         for ( i = 0 ; i < 3 ; i++ ) {
00705             point[i] = start[i] + frac * ( end[i] - start[i] );
00706         }
00707 
00708         VectorSubtract( point, f->origin, point );
00709 
00710         s = DotProduct( point, f->vectors[0] );
00711         t = 1.0 - DotProduct( point, f->vectors[1] );
00712         if ( s < 0 || s >= 1.0 || t < 0 || t >= 1.0 ) {
00713             continue;
00714         }
00715 
00716         // decide the filter size
00717         radius = 10 * frac;
00718         len = VectorLength( f->vectors[0] );
00719         if ( !len ) {
00720             continue;
00721         }
00722         radius = radius * len * f->si->width;
00723 
00724         // look up the filter, taking multiple samples
00725         VectorClear( total );
00726         for ( u = -1 ; u <= 1 ; u++ ) {
00727             for ( v = -1 ; v <=1 ; v++ ) {
00728                 x = s * f->si->width + u * radius;
00729                 if ( x < 0 ) {
00730                     x = 0;
00731                 }
00732                 if ( x >= f->si->width ) {
00733                     x = f->si->width - 1;
00734                 }
00735                 y = t * f->si->height + v * radius;
00736                 if ( y < 0 ) {
00737                     y = 0;
00738                 }
00739                 if ( y >= f->si->height ) {
00740                     y = f->si->height - 1;
00741                 }
00742 
00743                 pixel = f->si->pixels + ( y * f->si->width + x ) * 4;
00744                 total[0] += pixel[0];
00745                 total[1] += pixel[1];
00746                 total[2] += pixel[2];
00747             }
00748         }
00749 
00750         filter[0] *= total[0]/(255.0*9);
00751         filter[1] *= total[1]/(255.0*9);
00752         filter[2] *= total[2]/(255.0*9);
00753     }
00754 
00755 }
00756 
00757 /*
00758 ================
00759 SunToPoint
00760 
00761 Returns an amount of light to add at the point
00762 ================
00763 */
00764 int     c_sunHit, c_sunMiss;
00765 void SunToPoint( const vec3_t origin, traceWork_t *tw, vec3_t addLight ) {
00766     int         i;
00767     trace_t     trace;
00768     skyBrush_t  *b;
00769     vec3_t      end;
00770 
00771     if ( !numSkyBrushes ) {
00772         VectorClear( addLight );
00773         return;
00774     }
00775 
00776     VectorMA( origin, MAX_WORLD_COORD * 2, sunDirection, end );
00777 
00778     TraceLine( origin, end, &trace, qtrue, tw );
00779 
00780     // see if trace.hit is inside a sky brush
00781     for ( i = 0 ; i < numSkyBrushes ; i++) {
00782         b = &skyBrushes[ i ];
00783 
00784         // this assumes that sky brushes are axial...
00785         if (   trace.hit[0] < b->bounds[0][0] 
00786             || trace.hit[0] > b->bounds[1][0]
00787             || trace.hit[1] < b->bounds[0][1]
00788             || trace.hit[1] > b->bounds[1][1]
00789             || trace.hit[2] < b->bounds[0][2]
00790             || trace.hit[2] > b->bounds[1][2] ) {
00791             continue;
00792         }
00793 
00794 
00795         // trace again to get intermediate filters
00796         TraceLine( origin, trace.hit, &trace, qtrue, tw );
00797 
00798         // we hit the sky, so add sunlight
00799         if ( numthreads == 1 ) {
00800             c_sunHit++;
00801         }
00802         addLight[0] = trace.filter[0] * sunLight[0];
00803         addLight[1] = trace.filter[1] * sunLight[1];
00804         addLight[2] = trace.filter[2] * sunLight[2];
00805 
00806         return;
00807     }
00808 
00809     if ( numthreads == 1 ) {
00810         c_sunMiss++;
00811     }
00812 
00813     VectorClear( addLight );
00814 }
00815 
00816 /*
00817 ================
00818 SunToPlane
00819 ================
00820 */
00821 void SunToPlane( const vec3_t origin, const vec3_t normal, vec3_t color, traceWork_t *tw ) {
00822     float       angle;
00823     vec3_t      sunColor;
00824 
00825     if ( !numSkyBrushes ) {
00826         return;
00827     }
00828 
00829     angle = DotProduct( normal, sunDirection );
00830     if ( angle <= 0 ) {
00831         return;     // facing away
00832     }
00833 
00834     SunToPoint( origin, tw, sunColor );
00835     VectorMA( color, angle, sunColor, color );
00836 }
00837 
00838 /*
00839 ================
00840 LightingAtSample
00841 ================
00842 */
00843 void LightingAtSample( vec3_t origin, vec3_t normal, vec3_t color, 
00844                       qboolean testOcclusion, qboolean forceSunLight, traceWork_t *tw ) {
00845     light_t     *light;
00846     trace_t     trace;
00847     float       angle;
00848     float       add;
00849     float       dist;
00850     vec3_t      dir;
00851 
00852     VectorCopy( ambientColor, color );
00853 
00854     // trace to all the lights
00855     for ( light = lights ; light ; light = light->next ) {
00856 
00857         //MrE: if the light is behind the surface
00858         if ( DotProduct(light->origin, normal) - DotProduct(normal, origin) < 0 )
00859             continue;
00860         // testing exact PTPFF
00861         if ( exactPointToPolygon && light->type == emit_area ) {
00862             float       factor;
00863             float       d;
00864             vec3_t      pushedOrigin;
00865 
00866             // see if the point is behind the light
00867             d = DotProduct( origin, light->normal ) - light->dist;
00868             if ( !light->twosided ) {
00869                 if ( d < -1 ) {
00870                     continue;       // point is behind light
00871                 }
00872             }
00873 
00874             // test occlusion and find light filters
00875             // clip the line, tracing from the surface towards the light
00876             if ( !notrace && testOcclusion ) {
00877                 TraceLine( origin, light->origin, &trace, qfalse, tw );
00878 
00879                 // other light rays must not hit anything
00880                 if ( trace.passSolid ) {
00881                     continue;
00882                 }
00883             } else {
00884                 trace.filter[0] = 1.0;
00885                 trace.filter[1] = 1.0;
00886                 trace.filter[2] = 1.0;
00887             }
00888 
00889             // nudge the point so that it is clearly forward of the light
00890             // so that surfaces meeting a light emiter don't get black edges
00891             if ( d > -8 && d < 8 ) {
00892                 VectorMA( origin, (8-d), light->normal, pushedOrigin ); 
00893             } else {
00894                 VectorCopy( origin, pushedOrigin );
00895             }
00896 
00897             // calculate the contribution
00898             factor = PointToPolygonFormFactor( pushedOrigin, normal, light->w );
00899             if ( factor <= 0 ) {
00900                 if ( light->twosided ) {
00901                     factor = -factor;
00902                 } else {
00903                     continue;
00904                 }
00905             }
00906             color[0] += factor * light->emitColor[0] * trace.filter[0];
00907             color[1] += factor * light->emitColor[1] * trace.filter[1];
00908             color[2] += factor * light->emitColor[2] * trace.filter[2];
00909 
00910             continue;
00911         }
00912 
00913         // calculate the amount of light at this sample
00914         if ( light->type == emit_point ) {
00915             VectorSubtract( light->origin, origin, dir );
00916             dist = VectorNormalize( dir, dir );
00917             // clamp the distance to prevent super hot spots
00918             if ( dist < 16 ) {
00919                 dist = 16;
00920             }
00921             angle = DotProduct( normal, dir );
00922             if ( light->linearLight ) {
00923                 add = angle * light->photons * linearScale - dist;
00924                 if ( add < 0 ) {
00925                     add = 0;
00926                 }
00927             } else {
00928                 add = light->photons / ( dist * dist ) * angle;
00929             }
00930         } else if ( light->type == emit_spotlight ) {
00931             float   distByNormal;
00932             vec3_t  pointAtDist;
00933             float   radiusAtDist;
00934             float   sampleRadius;
00935             vec3_t  distToSample;
00936             float   coneScale;
00937 
00938             VectorSubtract( light->origin, origin, dir );
00939 
00940             distByNormal = -DotProduct( dir, light->normal );
00941             if ( distByNormal < 0 ) {
00942                 continue;
00943             }
00944             VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
00945             radiusAtDist = light->radiusByDist * distByNormal;
00946 
00947             VectorSubtract( origin, pointAtDist, distToSample );
00948             sampleRadius = VectorLength( distToSample );
00949 
00950             if ( sampleRadius >= radiusAtDist ) {
00951                 continue;       // outside the cone
00952             }
00953             if ( sampleRadius <= radiusAtDist - 32 ) {
00954                 coneScale = 1.0;    // fully inside
00955             } else {
00956                 coneScale = ( radiusAtDist - sampleRadius ) / 32.0;
00957             }
00958             
00959             dist = VectorNormalize( dir, dir );
00960             // clamp the distance to prevent super hot spots
00961             if ( dist < 16 ) {
00962                 dist = 16;
00963             }
00964             angle = DotProduct( normal, dir );
00965             add = light->photons / ( dist * dist ) * angle * coneScale;
00966 
00967         } else if ( light->type == emit_area ) {
00968             VectorSubtract( light->origin, origin, dir );
00969             dist = VectorNormalize( dir, dir );
00970             // clamp the distance to prevent super hot spots
00971             if ( dist < 16 ) {
00972                 dist = 16;
00973             }
00974             angle = DotProduct( normal, dir );
00975             if ( angle <= 0 ) {
00976                 continue;
00977             }
00978             angle *= -DotProduct( light->normal, dir );
00979             if ( angle <= 0 ) {
00980                 continue;
00981             }
00982 
00983             if ( light->linearLight ) {
00984                 add = angle * light->photons * linearScale - dist;
00985                 if ( add < 0 ) {
00986                     add = 0;
00987                 }
00988             } else {
00989                 add = light->photons / ( dist * dist ) * angle;
00990             }
00991         }
00992 
00993         if ( add <= 1.0 ) {
00994             continue;
00995         }
00996 
00997         // clip the line, tracing from the surface towards the light
00998         if ( !notrace && testOcclusion ) {
00999             TraceLine( origin, light->origin, &trace, qfalse, tw );
01000 
01001             // other light rays must not hit anything
01002             if ( trace.passSolid ) {
01003                 continue;
01004             }
01005         } else {
01006             trace.filter[0] = 1;
01007             trace.filter[1] = 1;
01008             trace.filter[2] = 1;
01009         }
01010         
01011         // add the result
01012         color[0] += add * light->color[0] * trace.filter[0];
01013         color[1] += add * light->color[1] * trace.filter[1];
01014         color[2] += add * light->color[2] * trace.filter[2];
01015     }
01016 
01017     //
01018     // trace directly to the sun
01019     //
01020     if ( testOcclusion || forceSunLight ) {
01021         SunToPlane( origin, normal, color, tw );
01022     }
01023 }
01024 
01025 /*
01026 =============
01027 PrintOccluded
01028 
01029 For debugging
01030 =============
01031 */
01032 void PrintOccluded( byte occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE], 
01033                    int width, int height ) {
01034     int i, j;
01035 
01036     _printf( "\n" );
01037 
01038     for ( i = 0 ; i < height ; i++ ) {
01039         for ( j = 0 ; j < width ; j++ ) {
01040             _printf("%i", (int)occluded[j][i] );
01041         }
01042         _printf( "\n" );
01043     }
01044 }
01045 
01046 
01047 /*
01048 =============
01049 VertexLighting
01050 
01051 Vertex lighting will completely ignore occlusion, because
01052 shadows would not be resolvable anyway.
01053 =============
01054 */
01055 void VertexLighting( dsurface_t *ds, qboolean testOcclusion, qboolean forceSunLight, float scale, traceWork_t *tw ) {
01056     int         i, j;
01057     drawVert_t  *dv;
01058     vec3_t      sample, normal;
01059     float       max;
01060 
01061     VectorCopy( ds->lightmapVecs[2], normal );
01062 
01063     // generate vertex lighting
01064     for ( i = 0 ; i < ds->numVerts ; i++ ) {
01065         dv = &drawVerts[ ds->firstVert + i ];
01066 
01067         if ( ds->patchWidth ) {
01068             LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
01069         }
01070         else if (ds->surfaceType == MST_TRIANGLE_SOUP) {
01071             LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
01072         }
01073         else {
01074             LightingAtSample( dv->xyz, normal, sample, testOcclusion, forceSunLight, tw );
01075         }
01076 
01077         if (scale >= 0)
01078             VectorScale(sample, scale, sample);
01079         // clamp with color normalization
01080         max = sample[0];
01081         if ( sample[1] > max ) {
01082             max = sample[1];
01083         }
01084         if ( sample[2] > max ) {
01085             max = sample[2];
01086         }
01087         if ( max > 255 ) {
01088             VectorScale( sample, 255/max, sample );
01089         }
01090 
01091         // save the sample
01092         for ( j = 0 ; j < 3 ; j++ ) {
01093             if ( sample[j] > 255 ) {
01094                 sample[j] = 255;
01095             }
01096             dv->color[j] = sample[j];
01097         }
01098 
01099         // Don't bother writing alpha since it will already be set to 255,
01100         // plus we don't want to write over alpha generated by SetTerrainTextures
01101         //dv->color[3] = 255;
01102     }
01103 }
01104 
01105 
01106 /*
01107 =================
01108 LinearSubdivideMesh
01109 
01110 For extra lighting, just midpoint one of the axis.
01111 The edges are clamped at the original edges.
01112 =================
01113 */
01114 mesh_t *LinearSubdivideMesh( mesh_t *in ) {
01115     int         i, j;
01116     mesh_t      *out;
01117     drawVert_t  *v1, *v2, *vout;
01118 
01119     out = malloc( sizeof( *out ) );
01120 
01121     out->width = in->width * 2;
01122     out->height = in->height;
01123     out->verts = malloc( out->width * out->height * sizeof(*out->verts) );
01124     for ( j = 0 ; j < in->height ; j++ ) {
01125         out->verts[ j * out->width + 0 ] = in->verts[ j * in->width + 0 ];
01126         out->verts[ j * out->width + out->width - 1 ] = in->verts[ j * in->width + in->width - 1 ];
01127         for ( i = 1 ; i < out->width - 1 ; i+= 2 ) {
01128             v1 = in->verts + j * in->width + (i >> 1);
01129             v2 = v1 + 1;
01130             vout = out->verts + j * out->width + i;
01131 
01132             vout->xyz[0] = 0.75 * v1->xyz[0] + 0.25 * v2->xyz[0];
01133             vout->xyz[1] = 0.75 * v1->xyz[1] + 0.25 * v2->xyz[1];
01134             vout->xyz[2] = 0.75 * v1->xyz[2] + 0.25 * v2->xyz[2];
01135 
01136             vout->normal[0] = 0.75 * v1->normal[0] + 0.25 * v2->normal[0];
01137             vout->normal[1] = 0.75 * v1->normal[1] + 0.25 * v2->normal[1];
01138             vout->normal[2] = 0.75 * v1->normal[2] + 0.25 * v2->normal[2];
01139 
01140             VectorNormalize( vout->normal, vout->normal );
01141 
01142             vout++;
01143 
01144             vout->xyz[0] = 0.25 * v1->xyz[0] + 0.75 * v2->xyz[0];
01145             vout->xyz[1] = 0.25 * v1->xyz[1] + 0.75 * v2->xyz[1];
01146             vout->xyz[2] = 0.25 * v1->xyz[2] + 0.75 * v2->xyz[2];
01147 
01148             vout->normal[0] = 0.25 * v1->normal[0] + 0.75 * v2->normal[0];
01149             vout->normal[1] = 0.25 * v1->normal[1] + 0.75 * v2->normal[1];
01150             vout->normal[2] = 0.25 * v1->normal[2] + 0.75 * v2->normal[2];
01151 
01152             VectorNormalize( vout->normal, vout->normal );
01153 
01154         }
01155     }
01156 
01157     FreeMesh( in );
01158 
01159     return out;
01160 }
01161 
01162 /*
01163 ==============
01164 ColorToBytes
01165 ==============
01166 */
01167 void ColorToBytes( const float *color, byte *colorBytes ) {
01168     float   max;
01169     vec3_t  sample;
01170 
01171     VectorCopy( color, sample );
01172 
01173     // clamp with color normalization
01174     max = sample[0];
01175     if ( sample[1] > max ) {
01176         max = sample[1];
01177     }
01178     if ( sample[2] > max ) {
01179         max = sample[2];
01180     }
01181     if ( max > 255 ) {
01182         VectorScale( sample, 255/max, sample );
01183     }
01184     colorBytes[ 0 ] = sample[0];
01185     colorBytes[ 1 ] = sample[1];
01186     colorBytes[ 2 ] = sample[2];
01187 }
01188 
01189 
01190 
01191 /*
01192 =============
01193 TraceLtm
01194 =============
01195 */
01196 void TraceLtm( int num ) {
01197     dsurface_t  *ds;
01198     int         i, j, k;
01199     int         x, y;
01200     int         position, numPositions;
01201     vec3_t      base, origin, normal;
01202     byte        occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
01203     vec3_t      color[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
01204     traceWork_t tw;
01205     vec3_t      average;
01206     int         count;
01207     mesh_t      srcMesh, *mesh, *subdivided;
01208     shaderInfo_t    *si;
01209     static float    nudge[2][9] = {
01210         { 0, -1, 0, 1, -1, 1, -1, 0, 1 },
01211         { 0, -1, -1, -1, 0, 0, 1, 1, 1 }
01212     };
01213     int         sampleWidth, sampleHeight, ssize;
01214     vec3_t      lightmapOrigin, lightmapVecs[2];
01215     int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_WIDTH];
01216 
01217     ds = &drawSurfaces[num];
01218     si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
01219 
01220     // vertex-lit triangle model
01221     if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
01222         VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
01223         return;
01224     }
01225     
01226     if ( ds->lightmapNum == -1 ) {
01227         return;     // doesn't need lighting at all
01228     }
01229 
01230     if (!novertexlighting) {
01231         // calculate the vertex lighting for gouraud shade mode
01232         VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
01233     }
01234 
01235     if ( ds->lightmapNum < 0 ) {
01236         return;     // doesn't need lightmap lighting
01237     }
01238 
01239     si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
01240     ssize = samplesize;
01241     if (si->lightmapSampleSize)
01242         ssize = si->lightmapSampleSize;
01243 
01244     if (si->patchShadows)
01245         tw.patchshadows = qtrue;
01246     else
01247         tw.patchshadows = patchshadows;
01248 
01249     if ( ds->surfaceType == MST_PATCH ) {
01250         srcMesh.width = ds->patchWidth;
01251         srcMesh.height = ds->patchHeight;
01252         srcMesh.verts = drawVerts + ds->firstVert;
01253         mesh = SubdivideMesh( srcMesh, 8, 999 );
01254         PutMeshOnCurve( *mesh );
01255         MakeMeshNormals( *mesh );
01256 
01257         subdivided = RemoveLinearMeshColumnsRows( mesh );
01258         FreeMesh(mesh);
01259 
01260         mesh = SubdivideMeshQuads( subdivided, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
01261         if ( mesh->width != ds->lightmapWidth || mesh->height != ds->lightmapHeight ) {
01262             Error( "Mesh lightmap miscount");
01263         }
01264 
01265         if ( extra ) {
01266             mesh_t  *mp;
01267 
01268             // chop it up for more light samples (leaking memory...)
01269             mp = mesh;//CopyMesh( mesh );
01270             mp = LinearSubdivideMesh( mp );
01271             mp = TransposeMesh( mp );
01272             mp = LinearSubdivideMesh( mp );
01273             mp = TransposeMesh( mp );
01274 
01275             mesh = mp;
01276         }
01277     } else {
01278         VectorCopy( ds->lightmapVecs[2], normal );
01279 
01280         if ( !extra ) {
01281             VectorCopy( ds->lightmapOrigin, lightmapOrigin );
01282             VectorCopy( ds->lightmapVecs[0], lightmapVecs[0] );
01283             VectorCopy( ds->lightmapVecs[1], lightmapVecs[1] );
01284         } else {
01285             // sample at a closer spacing for antialiasing
01286             VectorCopy( ds->lightmapOrigin, lightmapOrigin );
01287             VectorScale( ds->lightmapVecs[0], 0.5, lightmapVecs[0] );
01288             VectorScale( ds->lightmapVecs[1], 0.5, lightmapVecs[1] );
01289             VectorMA( lightmapOrigin, -0.5, lightmapVecs[0], lightmapOrigin );
01290             VectorMA( lightmapOrigin, -0.5, lightmapVecs[1], lightmapOrigin );
01291         }
01292     }
01293 
01294     if ( extra ) {
01295         sampleWidth = ds->lightmapWidth * 2;
01296         sampleHeight = ds->lightmapHeight * 2;
01297     } else {
01298         sampleWidth = ds->lightmapWidth;
01299         sampleHeight = ds->lightmapHeight;
01300     }
01301 
01302     memset ( color, 0, sizeof( color ) );
01303 
01304     // determine which samples are occluded
01305     memset ( occluded, 0, sizeof( occluded ) );
01306     for ( i = 0 ; i < sampleWidth ; i++ ) {
01307         for ( j = 0 ; j < sampleHeight ; j++ ) {
01308 
01309             if ( ds->patchWidth ) {
01310                 numPositions = 9;
01311                 VectorCopy( mesh->verts[j*mesh->width+i].normal, normal );
01312                 // VectorNormalize( normal, normal );
01313                 // push off of the curve a bit
01314                 VectorMA( mesh->verts[j*mesh->width+i].xyz, 1, normal, base );
01315 
01316                 MakeNormalVectors( normal, lightmapVecs[0], lightmapVecs[1] );
01317             } else {
01318                 numPositions = 9;
01319                 for ( k = 0 ; k < 3 ; k++ ) {
01320                     base[k] = lightmapOrigin[k] + normal[k]
01321                         + i * lightmapVecs[0][k] 
01322                         + j * lightmapVecs[1][k];
01323                 }
01324             }
01325             VectorAdd( base, surfaceOrigin[ num ], base );
01326 
01327             // we may need to slightly nudge the sample point
01328             // if directly on a wall
01329             for ( position = 0 ; position < numPositions ; position++ ) {
01330                 // calculate lightmap sample position
01331                 for ( k = 0 ; k < 3 ; k++ ) {
01332                     origin[k] = base[k] + 
01333                         + ( nudge[0][position]/16 ) * lightmapVecs[0][k] 
01334                         + ( nudge[1][position]/16 ) * lightmapVecs[1][k];
01335                 }
01336 
01337                 if ( notrace ) {
01338                     break;
01339                 }
01340                 if ( !PointInSolid( origin ) ) {
01341                     break;
01342                 }
01343             }
01344 
01345             // if none of the nudges worked, this sample is occluded
01346             if ( position == numPositions ) {
01347                 occluded[i][j] = qtrue;
01348                 if ( numthreads == 1 ) {
01349                     c_occluded++;
01350                 }
01351                 continue;
01352             }
01353             
01354             if ( numthreads == 1 ) {
01355                 c_visible++;
01356             }
01357             occluded[i][j] = qfalse;
01358             LightingAtSample( origin, normal, color[i][j], qtrue, qfalse, &tw );
01359         }
01360     }
01361 
01362     if ( dump ) {
01363         PrintOccluded( occluded, sampleWidth, sampleHeight );
01364     }
01365 
01366     // calculate average values for occluded samples
01367     for ( i = 0 ; i < sampleWidth ; i++ ) {
01368         for ( j = 0 ; j < sampleHeight ; j++ ) {
01369             if ( !occluded[i][j] ) {
01370                 continue;
01371             }
01372             // scan all surrounding samples
01373             count = 0;
01374             VectorClear( average );
01375             for ( x = -1 ; x <= 1; x++ ) {
01376                 for ( y = -1 ; y <= 1 ; y++ ) {
01377                     if ( i + x < 0 || i + x >= sampleWidth ) {
01378                         continue;
01379                     }
01380                     if ( j + y < 0 || j + y >= sampleHeight ) {
01381                         continue;
01382                     }
01383                     if ( occluded[i+x][j+y] ) {
01384                         continue;
01385                     }
01386                     count++;
01387                     VectorAdd( color[i+x][j+y], average, average );
01388                 }
01389             }
01390             if ( count ) {
01391                 VectorScale( average, 1.0/count, color[i][j] );
01392             }
01393         }
01394     }
01395 
01396     // average together the values if we are extra sampling
01397     if ( ds->lightmapWidth != sampleWidth ) {
01398         for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
01399             for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
01400                 for ( k = 0 ; k < 3 ; k++ ) {
01401                     float       value, coverage;
01402 
01403                     value = color[i*2][j*2][k] + color[i*2][j*2+1][k] +
01404                         color[i*2+1][j*2][k] + color[i*2+1][j*2+1][k];
01405                     coverage = 4;
01406                     if ( extraWide ) {
01407                         // wider than box filter
01408                         if ( i > 0 ) {
01409                             value += color[i*2-1][j*2][k] + color[i*2-1][j*2+1][k];
01410                             value += color[i*2-2][j*2][k] + color[i*2-2][j*2+1][k];
01411                             coverage += 4;
01412                         }
01413                         if ( i < ds->lightmapWidth - 1 ) {
01414                             value += color[i*2+2][j*2][k] + color[i*2+2][j*2+1][k];
01415                             value += color[i*2+3][j*2][k] + color[i*2+3][j*2+1][k];
01416                             coverage += 4;
01417                         }
01418                         if ( j > 0 ) {
01419                             value += color[i*2][j*2-1][k] + color[i*2+1][j*2-1][k];
01420                             value += color[i*2][j*2-2][k] + color[i*2+1][j*2-2][k];
01421                             coverage += 4;
01422                         }
01423                         if ( j < ds->lightmapHeight - 1 ) {
01424                             value += color[i*2][j*2+2][k] + color[i*2+1][j*2+2][k];
01425                             value += color[i*2][j*2+3][k] + color[i*2+1][j*2+3][k];
01426                             coverage += 2;
01427                         }
01428                     }
01429 
01430                     color[i][j][k] = value / coverage;
01431                 }
01432             }
01433         }
01434     }
01435 
01436     // optionally create a debugging border around the lightmap
01437     if ( lightmapBorder ) {
01438         for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
01439             color[i][0][0] = 255;
01440             color[i][0][1] = 0;
01441             color[i][0][2] = 0;
01442 
01443             color[i][ds->lightmapHeight-1][0] = 255;
01444             color[i][ds->lightmapHeight-1][1] = 0;
01445             color[i][ds->lightmapHeight-1][2] = 0;
01446         }
01447         for ( i = 0 ; i < ds->lightmapHeight ; i++ ) {
01448             color[0][i][0] = 255;
01449             color[0][i][1] = 0;
01450             color[0][i][2] = 0;
01451 
01452             color[ds->lightmapWidth-1][i][0] = 255;
01453             color[ds->lightmapWidth-1][i][1] = 0;
01454             color[ds->lightmapWidth-1][i][2] = 0;
01455         }
01456     }
01457 
01458     // clamp the colors to bytes and store off
01459     for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
01460         for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
01461             k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + j) 
01462                 * LIGHTMAP_WIDTH + ds->lightmapX + i;
01463 
01464             ColorToBytes( color[i][j], lightBytes + k*3 );
01465         }
01466     }
01467 
01468     if (ds->surfaceType == MST_PATCH)
01469     {
01470         FreeMesh(mesh);
01471     }
01472 }
01473 
01474 
01475 //=============================================================================
01476 
01477 vec3_t  gridMins;
01478 vec3_t  gridSize = { 64, 64, 128 };
01479 int     gridBounds[3];
01480 
01481 
01482 /*
01483 ========================
01484 LightContributionToPoint
01485 ========================
01486 */
01487 qboolean LightContributionToPoint( const light_t *light, const vec3_t origin,
01488                                   vec3_t color, traceWork_t *tw ) {
01489     trace_t     trace;
01490     float       add;
01491 
01492     add = 0;
01493 
01494     VectorClear( color );
01495 
01496     // testing exact PTPFF
01497     if ( exactPointToPolygon && light->type == emit_area ) {
01498         float       factor;
01499         float       d;
01500         vec3_t      normal;
01501 
01502         // see if the point is behind the light
01503         d = DotProduct( origin, light->normal ) - light->dist;
01504         if ( !light->twosided ) {
01505             if ( d < 1 ) {
01506                 return qfalse;      // point is behind light
01507             }
01508         }
01509 
01510         // test occlusion
01511         // clip the line, tracing from the surface towards the light
01512         TraceLine( origin, light->origin, &trace, qfalse, tw );
01513         if ( trace.passSolid ) {
01514             return qfalse;
01515         }
01516 
01517         // calculate the contribution
01518         VectorSubtract( light->origin, origin, normal );
01519         if ( VectorNormalize( normal, normal ) == 0 ) {
01520             return qfalse;
01521         }
01522         factor = PointToPolygonFormFactor( origin, normal, light->w );
01523         if ( factor <= 0 ) {
01524             if ( light->twosided ) {
01525                 factor = -factor;
01526             } else {
01527                 return qfalse;
01528             }
01529         }
01530         VectorScale( light->emitColor, factor, color );
01531         return qtrue;
01532     }
01533 
01534     // calculate the amount of light at this sample
01535     if ( light->type == emit_point || light->type == emit_spotlight ) {
01536         vec3_t      dir;
01537         float       dist;
01538 
01539         VectorSubtract( light->origin, origin, dir );
01540         dist = VectorLength( dir );
01541         // clamp the distance to prevent super hot spots
01542         if ( dist < 16 ) {
01543             dist = 16;
01544         }
01545         if ( light->linearLight ) {
01546             add = light->photons * linearScale - dist;
01547             if ( add < 0 ) {
01548                 add = 0;
01549             }
01550         } else {
01551             add = light->photons / ( dist * dist );
01552         }
01553     } else {
01554         return qfalse;
01555     }
01556 
01557     if ( add <= 1.0 ) {
01558         return qfalse;
01559     }
01560 
01561     // clip the line, tracing from the surface towards the light
01562     TraceLine( origin, light->origin, &trace, qfalse, tw );
01563 
01564     // other light rays must not hit anything
01565     if ( trace.passSolid ) {
01566         return qfalse;
01567     }
01568 
01569     // add the result
01570     color[0] = add * light->color[0];
01571     color[1] = add * light->color[1];
01572     color[2] = add * light->color[2];
01573 
01574     return qtrue;
01575 }
01576 
01577 typedef struct {
01578     vec3_t      dir;
01579     vec3_t      color;
01580 } contribution_t;
01581 
01582 /*
01583 =============
01584 TraceGrid
01585 
01586 Grid samples are foe quickly determining the lighting
01587 of dynamically placed entities in the world
01588 =============
01589 */
01590 #define MAX_CONTRIBUTIONS   1024
01591 void TraceGrid( int num ) {
01592     int         x, y, z;
01593     vec3_t      origin;
01594     light_t     *light;
01595     vec3_t      color;
01596     int         mod;
01597     vec3_t      directedColor;
01598     vec3_t      summedDir;
01599     contribution_t  contributions[MAX_CONTRIBUTIONS];
01600     int         numCon;
01601     int         i;
01602     traceWork_t tw;
01603     float       addSize;
01604 
01605     mod = num;
01606     z = mod / ( gridBounds[0] * gridBounds[1] );
01607     mod -= z * ( gridBounds[0] * gridBounds[1] );
01608 
01609     y = mod / gridBounds[0];
01610     mod -= y * gridBounds[0];
01611 
01612     x = mod;
01613 
01614     origin[0] = gridMins[0] + x * gridSize[0];
01615     origin[1] = gridMins[1] + y * gridSize[1];
01616     origin[2] = gridMins[2] + z * gridSize[2];
01617 
01618     if ( PointInSolid( origin ) ) {
01619         vec3_t  baseOrigin;
01620         int     step;
01621 
01622         VectorCopy( origin, baseOrigin );
01623 
01624         // try to nudge the origin around to find a valid point
01625         for ( step = 9 ; step <= 18 ; step += 9 ) {
01626             for ( i = 0 ; i < 8 ; i++ ) {
01627                 VectorCopy( baseOrigin, origin );
01628                 if ( i & 1 ) {
01629                     origin[0] += step;
01630                 } else {
01631                     origin[0] -= step;
01632                 }
01633                 if ( i & 2 ) {
01634                     origin[1] += step;
01635                 } else {
01636                     origin[1] -= step;
01637                 }
01638                 if ( i & 4 ) {
01639                     origin[2] += step;
01640                 } else {
01641                     origin[2] -= step;
01642                 }
01643 
01644                 if ( !PointInSolid( origin ) ) {
01645                     break;
01646                 }
01647             }
01648             if ( i != 8 ) {
01649                 break;
01650             }
01651         }
01652         if ( step > 18 ) {
01653             // can't find a valid point at all
01654             for ( i = 0 ; i < 8 ; i++ ) {
01655                 gridData[ num*8 + i ] = 0;
01656             }
01657             return;
01658         }
01659     }
01660 
01661     VectorClear( summedDir );
01662 
01663     // trace to all the lights
01664 
01665     // find the major light direction, and divide the
01666     // total light between that along the direction and
01667     // the remaining in the ambient 
01668     numCon = 0;
01669     for ( light = lights ; light ; light = light->next ) {
01670         vec3_t      add;
01671         vec3_t      dir;
01672         float       addSize;
01673 
01674         if ( !LightContributionToPoint( light, origin, add, &tw ) ) {
01675             continue;
01676         }
01677 
01678         VectorSubtract( light->origin, origin, dir );
01679         VectorNormalize( dir, dir );
01680 
01681         VectorCopy( add, contributions[numCon].color );
01682         VectorCopy( dir, contributions[numCon].dir );
01683         numCon++;
01684 
01685         addSize = VectorLength( add );
01686         VectorMA( summedDir, addSize, dir, summedDir );
01687 
01688         if ( numCon == MAX_CONTRIBUTIONS-1 ) {
01689             break;
01690         }
01691     }
01692 
01693     //
01694     // trace directly to the sun
01695     //
01696     SunToPoint( origin, &tw, color );
01697     addSize = VectorLength( color );
01698     if ( addSize > 0 ) {
01699         VectorCopy( color, contributions[numCon].color );
01700         VectorCopy( sunDirection, contributions[numCon].dir );
01701         VectorMA( summedDir, addSize, sunDirection, summedDir );
01702         numCon++;
01703     }
01704 
01705 
01706     // now that we have identified the primary light direction,
01707     // go back and seperate all the light into directed and ambient
01708     VectorNormalize( summedDir, summedDir );
01709     VectorCopy( ambientColor, color );
01710     VectorClear( directedColor );
01711 
01712     for ( i = 0 ; i < numCon ; i++ ) {
01713         float   d;
01714 
01715         d = DotProduct( contributions[i].dir, summedDir );
01716         if ( d < 0 ) {
01717             d = 0;
01718         }
01719 
01720         VectorMA( directedColor, d, contributions[i].color, directedColor );
01721 
01722         // the ambient light will be at 1/4 the value of directed light
01723         d = 0.25 * ( 1.0 - d );
01724         VectorMA( color, d, contributions[i].color, color );
01725     }
01726 
01727     // now do some fudging to keep the ambient from being too low
01728     VectorMA( color, 0.25, directedColor, color );
01729 
01730     //
01731     // save the resulting value out
01732     //
01733     ColorToBytes( color, gridData + num*8 );
01734     ColorToBytes( directedColor, gridData + num*8 + 3 );
01735 
01736     VectorNormalize( summedDir, summedDir );
01737     NormalToLatLong( summedDir, gridData + num*8 + 6);
01738 }
01739 
01740 
01741 /*
01742 =============
01743 SetupGrid
01744 =============
01745 */
01746 void SetupGrid( void ) {
01747     int     i;
01748     vec3_t  maxs;
01749 
01750     for ( i = 0 ; i < 3 ; i++ ) {
01751         gridMins[i] = gridSize[i] * ceil( dmodels[0].mins[i] / gridSize[i] );
01752         maxs[i] = gridSize[i] * floor( dmodels[0].maxs[i] / gridSize[i] );
01753         gridBounds[i] = (maxs[i] - gridMins[i])/gridSize[i] + 1;
01754     }
01755 
01756     numGridPoints = gridBounds[0] * gridBounds[1] * gridBounds[2];
01757     if (numGridPoints * 8 >= MAX_MAP_LIGHTGRID)
01758         Error("MAX_MAP_LIGHTGRID");
01759     qprintf( "%5i gridPoints\n", numGridPoints );
01760 }
01761 
01762 //=============================================================================
01763 
01764 /*
01765 =============
01766 RemoveLightsInSolid
01767 =============
01768 */
01769 void RemoveLightsInSolid(void)
01770 {
01771     light_t *light, *prev;
01772     int numsolid = 0;
01773 
01774     prev = NULL;
01775     for ( light = lights ; light ;  ) {
01776         if (PointInSolid(light->origin))
01777         {
01778             if (prev) prev->next = light->next;
01779             else lights = light->next;
01780             if (light->w)
01781                 FreeWinding(light->w);
01782             free(light);
01783             numsolid++;
01784             if (prev)
01785                 light = prev->next;
01786             else
01787                 light = lights;
01788         }
01789         else
01790         {
01791             prev = light;
01792             light = light->next;
01793         }
01794     }
01795     _printf (" %7i lights in solid\n", numsolid);
01796 }
01797 
01798 /*
01799 =============
01800 LightWorld
01801 =============
01802 */
01803 void LightWorld (void) {
01804     float       f;
01805 
01806     // determine the number of grid points
01807     SetupGrid();
01808 
01809     // find the optional world ambient
01810     GetVectorForKey( &entities[0], "_color", ambientColor );
01811     f = FloatForKey( &entities[0], "ambient" );
01812     VectorScale( ambientColor, f, ambientColor );
01813 
01814     // create lights out of patches and lights
01815     qprintf ("--- CreateLights ---\n");
01816     CreateEntityLights ();
01817     qprintf ("%i point lights\n", numPointLights);
01818     qprintf ("%i area lights\n", numAreaLights);
01819 
01820     if (!nogridlighting) {
01821         qprintf ("--- TraceGrid ---\n");
01822         RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
01823         qprintf( "%i x %i x %i = %i grid\n", gridBounds[0], gridBounds[1],
01824             gridBounds[2], numGridPoints);
01825     }
01826 
01827     qprintf ("--- TraceLtm ---\n");
01828     RunThreadsOnIndividual( numDrawSurfaces, qtrue, TraceLtm );
01829     qprintf( "%5i visible samples\n", c_visible );
01830     qprintf( "%5i occluded samples\n", c_occluded );
01831 }
01832 
01833 /*
01834 ========
01835 CreateFilters
01836 
01837 EXPERIMENTAL, UNUSED
01838 
01839 Look for transparent light filter surfaces.
01840 
01841 This will only work for flat 3*3 patches that exactly hold one copy of the texture.
01842 ========
01843 */
01844 #define PLANAR_PATCH_EPSILON    0.1
01845 void CreateFilters( void ) {
01846     int             i;
01847     filter_t        *f;
01848     dsurface_t      *ds;
01849     shaderInfo_t    *si;
01850     drawVert_t      *v1, *v2, *v3;
01851     vec3_t          d1, d2;
01852     int             vertNum;
01853 
01854     numFilters = 0;
01855 
01856     return;
01857 
01858     for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
01859         ds = &drawSurfaces[i];
01860         if ( !ds->patchWidth ) {
01861             continue;
01862         }
01863         si = ShaderInfoForShader( dshaders[ ds->shaderNum ].shader );
01864 /*
01865         if ( !(si->surfaceFlags & SURF_LIGHTFILTER) ) {
01866             continue;
01867         }
01868 */
01869 
01870         // we have a filter patch
01871         v1 = &drawVerts[ ds->firstVert ];
01872 
01873         if ( ds->patchWidth != 3 || ds->patchHeight != 3 ) {
01874             _printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't a 3 by 3\n",
01875                 v1->xyz[0], v1->xyz[1], v1->xyz[2] );
01876             continue;
01877         }
01878 
01879         if ( numFilters == MAX_FILTERS ) {
01880             Error( "MAX_FILTERS" );
01881         }
01882         f = &filters[ numFilters ];
01883         numFilters++;
01884 
01885         v2 = &drawVerts[ ds->firstVert + 2 ];
01886         v3 = &drawVerts[ ds->firstVert + 6 ];
01887 
01888         VectorSubtract( v2->xyz, v1->xyz, d1 );
01889         VectorSubtract( v3->xyz, v1->xyz, d2 );
01890         VectorNormalize( d1, d1 );
01891         VectorNormalize( d2, d2 );
01892         CrossProduct( d1, d2, f->plane );
01893         f->plane[3] = DotProduct( v1->xyz, f->plane );
01894 
01895         // make sure all the control points are on the plane
01896         for ( vertNum = 0 ; vertNum < ds->numVerts ; vertNum++ ) {
01897             float   d;
01898 
01899             d = DotProduct( drawVerts[ ds->firstVert + vertNum ].xyz, f->plane ) - f->plane[3];
01900             if ( fabs( d ) > PLANAR_PATCH_EPSILON ) {
01901                 break;
01902             }
01903         }
01904         if ( vertNum != ds->numVerts ) {
01905             numFilters--;
01906             _printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't flat\n",
01907                 v1->xyz[0], v1->xyz[1], v1->xyz[2] );
01908             continue;
01909         }
01910     }
01911 
01912     f = &filters[0];
01913     numFilters = 1;
01914 
01915     f->plane[0] = 1;
01916     f->plane[1] = 0;
01917     f->plane[2] = 0;
01918     f->plane[3] = 448;
01919 
01920     f->origin[0] = 448;
01921     f->origin[1] = 192;
01922     f->origin[2] = 0;
01923 
01924     f->vectors[0][0] = 0;
01925     f->vectors[0][1] = -1.0 / 128;
01926     f->vectors[0][2] = 0;
01927 
01928     f->vectors[1][0] = 0;
01929     f->vectors[1][1] = 0;
01930     f->vectors[1][2] = 1.0 / 128;
01931 
01932     f->si = ShaderInfoForShader( "textures/hell/blocks11ct" );
01933 }
01934 
01935 /*
01936 =============
01937 VertexLightingThread
01938 =============
01939 */
01940 void VertexLightingThread(int num) {
01941     dsurface_t  *ds;
01942     traceWork_t tw;
01943     shaderInfo_t *si;
01944 
01945     ds = &drawSurfaces[num];
01946 
01947     // vertex-lit triangle model
01948     if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
01949         return;
01950     }
01951 
01952     if (novertexlighting)
01953         return;
01954 
01955     if ( ds->lightmapNum == -1 ) {
01956         return; // doesn't need lighting at all
01957     }
01958 
01959     si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
01960 
01961     // calculate the vertex lighting for gouraud shade mode
01962     VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
01963 }
01964 
01965 /*
01966 =============
01967 TriSoupLightingThread
01968 =============
01969 */
01970 void TriSoupLightingThread(int num) {
01971     dsurface_t  *ds;
01972     traceWork_t tw;
01973     shaderInfo_t *si;
01974 
01975     ds = &drawSurfaces[num];
01976     si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
01977 
01978     // vertex-lit triangle model
01979     if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
01980         VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
01981     }
01982 }
01983 
01984 /*
01985 =============
01986 GridAndVertexLighting
01987 =============
01988 */
01989 void GridAndVertexLighting(void) {
01990     SetupGrid();
01991 
01992     FindSkyBrushes();
01993     CreateFilters();
01994     InitTrace();
01995     CreateEntityLights ();
01996     CreateSurfaceLights();
01997 
01998     if (!nogridlighting) {
01999         _printf ("--- TraceGrid ---\n");
02000         RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
02001     }
02002 
02003     if (!novertexlighting) {
02004         _printf ("--- Vertex Lighting ---\n");
02005         RunThreadsOnIndividual( numDrawSurfaces, qtrue, VertexLightingThread );
02006     }
02007 
02008     _printf("--- Model Lighting ---\n");
02009     RunThreadsOnIndividual( numDrawSurfaces, qtrue, TriSoupLightingThread );
02010 }
02011 
02012 /*
02013 ========
02014 LightMain
02015 
02016 ========
02017 */
02018 int LightMain (int argc, char **argv) {
02019     int         i;
02020     double      start, end;
02021     const char  *value;
02022 
02023     _printf ("----- Lighting ----\n");
02024 
02025     verbose = qfalse;
02026 
02027     for (i=1 ; i<argc ; i++) {
02028         if (!strcmp(argv[i],"-tempname"))
02029     {
02030       i++;
02031     } else if (!strcmp(argv[i],"-v")) {
02032             verbose = qtrue;
02033         } else if (!strcmp(argv[i],"-threads")) {
02034             numthreads = atoi (argv[i+1]);
02035             i++;
02036         } else if (!strcmp(argv[i],"-area")) {
02037             areaScale *= atof(argv[i+1]);
02038             _printf ("area light scaling at %f\n", areaScale);
02039             i++;
02040         } else if (!strcmp(argv[i],"-point")) {
02041             pointScale *= atof(argv[i+1]);
02042             _printf ("point light scaling at %f\n", pointScale);
02043             i++;
02044         } else if (!strcmp(argv[i],"-notrace")) {
02045             notrace = qtrue;
02046             _printf ("No occlusion tracing\n");
02047         } else if (!strcmp(argv[i],"-patchshadows")) {
02048             patchshadows = qtrue;
02049             _printf ("Patch shadow casting enabled\n");
02050         } else if (!strcmp(argv[i],"-extra")) {
02051             extra = qtrue;
02052             _printf ("Extra detail tracing\n");
02053         } else if (!strcmp(argv[i],"-extrawide")) {
02054             extra = qtrue;
02055             extraWide = qtrue;
02056             _printf ("Extra wide detail tracing\n");
02057         } else if (!strcmp(argv[i], "-samplesize")) {
02058             samplesize = atoi(argv[i+1]);
02059             if (samplesize < 1) samplesize = 1;
02060             i++;
02061             _printf("lightmap sample size is %dx%d units\n", samplesize, samplesize);
02062         } else if (!strcmp(argv[i], "-novertex")) {
02063             novertexlighting = qtrue;
02064             _printf("no vertex lighting = true\n");
02065         } else if (!strcmp(argv[i], "-nogrid")) {
02066             nogridlighting = qtrue;
02067             _printf("no grid lighting = true\n");
02068         } else if (!strcmp(argv[i],"-border")) {
02069             lightmapBorder = qtrue;
02070             _printf ("Adding debug border to lightmaps\n");
02071         } else if (!strcmp(argv[i],"-nosurf")) {
02072             noSurfaces = qtrue;
02073             _printf ("Not tracing against surfaces\n" );
02074         } else if (!strcmp(argv[i],"-dump")) {
02075             dump = qtrue;
02076             _printf ("Dumping occlusion maps\n");
02077         } else {
02078             break;
02079         }
02080     }
02081 
02082     ThreadSetDefault ();
02083 
02084     if (i != argc - 1) {
02085         _printf("usage: q3map -light [-<switch> [-<switch> ...]] <mapname>\n"
02086                 "\n"
02087                 "Switches:\n"
02088                 "   v              = verbose output\n"
02089                 "   threads <X>    = set number of threads to X\n"
02090                 "   area <V>       = set the area light scale to V\n"
02091                 "   point <W>      = set the point light scale to W\n"
02092                 "   notrace        = don't cast any shadows\n"
02093                 "   extra          = enable super sampling for anti-aliasing\n"
02094                 "   extrawide      = same as extra but smoothen more\n"
02095                 "   nogrid         = don't calculate light grid for dynamic model lighting\n"
02096                 "   novertex       = don't calculate vertex lighting\n"
02097                 "   samplesize <N> = set the lightmap pixel size to NxN units\n");
02098         exit(0);
02099     }
02100 
02101     start = I_FloatTime ();
02102 
02103     SetQdirFromPath (argv[i]);  
02104 
02105 #ifdef _WIN32
02106     InitPakFile(gamedir, NULL);
02107 #endif
02108 
02109     strcpy (source, ExpandArg(argv[i]));
02110     StripExtension (source);
02111     DefaultExtension (source, ".bsp");
02112 
02113     LoadShaderInfo();
02114 
02115     _printf ("reading %s\n", source);
02116 
02117     LoadBSPFile (source);
02118 
02119     FindSkyBrushes();
02120 
02121     ParseEntities();
02122 
02123     value = ValueForKey( &entities[0], "gridsize" );
02124     if (strlen(value)) {
02125         sscanf( value, "%f %f %f", &gridSize[0], &gridSize[1], &gridSize[2] );
02126         _printf("grid size = {%1.1f, %1.1f, %1.1f}\n", gridSize[0], gridSize[1], gridSize[2]);
02127     }
02128 
02129     CreateFilters();
02130 
02131     InitTrace();
02132 
02133     SetEntityOrigins();
02134 
02135     CountLightmaps();
02136 
02137     CreateSurfaceLights();
02138 
02139     LightWorld();
02140 
02141     _printf ("writing %s\n", source);
02142     WriteBSPFile (source);
02143 
02144     end = I_FloatTime ();
02145     _printf ("%5.0f seconds elapsed\n", end-start);
02146     
02147     return 0;
02148 }
02149 

Generated on Thu Aug 25 12:38:20 2005 for Quake III Arena by  doxygen 1.3.9.1