12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150 |
- /*
- ===========================================================================
- Copyright (C) 1999-2005 Id Software, Inc.
- This file is part of Quake III Arena source code.
- Quake III Arena source code is free software; you can redistribute it
- and/or modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
- Quake III Arena source code is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Foobar; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- ===========================================================================
- */
- // light.c
- #include "light.h"
- #ifdef _WIN32
- #ifdef _TTIMOBUILD
- #include "pakstuff.h"
- #else
- #include "../libs/pakstuff.h"
- #endif
- #endif
- #define EXTRASCALE 2
- typedef struct {
- float plane[4];
- vec3_t origin;
- vec3_t vectors[2];
- shaderInfo_t *si;
- } filter_t;
- #define MAX_FILTERS 1024
- filter_t filters[MAX_FILTERS];
- int numFilters;
- extern char source[1024];
- qboolean notrace;
- qboolean patchshadows;
- qboolean dump;
- qboolean extra;
- qboolean extraWide;
- qboolean lightmapBorder;
- qboolean noSurfaces;
- int samplesize = 16; //sample size in units
- int novertexlighting = 0;
- int nogridlighting = 0;
- // for run time tweaking of all area sources in the level
- float areaScale = 0.25;
- // for run time tweaking of all point sources in the level
- float pointScale = 7500;
- qboolean exactPointToPolygon = qtrue;
- float formFactorValueScale = 3;
- float linearScale = 1.0 / 8000;
- light_t *lights;
- int numPointLights;
- int numAreaLights;
- FILE *dumpFile;
- int c_visible, c_occluded;
- //int defaultLightSubdivide = 128; // vary by surface size?
- int defaultLightSubdivide = 999; // vary by surface size?
- vec3_t ambientColor;
- vec3_t surfaceOrigin[ MAX_MAP_DRAW_SURFS ];
- int entitySurface[ MAX_MAP_DRAW_SURFS ];
- // 7,9,11 normalized to avoid being nearly coplanar with common faces
- //vec3_t sunDirection = { 0.441835, 0.56807, 0.694313 };
- //vec3_t sunDirection = { 0.45, 0, 0.9 };
- //vec3_t sunDirection = { 0, 0, 1 };
- // these are usually overrided by shader values
- vec3_t sunDirection = { 0.45, 0.3, 0.9 };
- vec3_t sunLight = { 100, 100, 50 };
- typedef struct {
- dbrush_t *b;
- vec3_t bounds[2];
- } skyBrush_t;
- int numSkyBrushes;
- skyBrush_t skyBrushes[MAX_MAP_BRUSHES];
- /*
- the corners of a patch mesh will always be exactly at lightmap samples.
- The dimensions of the lightmap will be equal to the average length of the control
- mesh in each dimension divided by 2.
- The lightmap sample points should correspond to the chosen subdivision points.
- */
- /*
- ===============================================================
- SURFACE LOADING
- ===============================================================
- */
- #define MAX_FACE_POINTS 128
- /*
- ===============
- SubdivideAreaLight
- Subdivide area lights that are very large
- A light that is subdivided will never backsplash, avoiding weird pools of light near edges
- ===============
- */
- void SubdivideAreaLight( shaderInfo_t *ls, winding_t *w, vec3_t normal,
- float areaSubdivide, qboolean backsplash ) {
- float area, value, intensity;
- light_t *dl, *dl2;
- vec3_t mins, maxs;
- int axis;
- winding_t *front, *back;
- vec3_t planeNormal;
- float planeDist;
- if ( !w ) {
- return;
- }
- WindingBounds( w, mins, maxs );
- // check for subdivision
- for ( axis = 0 ; axis < 3 ; axis++ ) {
- if ( maxs[axis] - mins[axis] > areaSubdivide ) {
- VectorClear( planeNormal );
- planeNormal[axis] = 1;
- planeDist = ( maxs[axis] + mins[axis] ) * 0.5;
- ClipWindingEpsilon ( w, planeNormal, planeDist, ON_EPSILON, &front, &back );
- SubdivideAreaLight( ls, front, normal, areaSubdivide, qfalse );
- SubdivideAreaLight( ls, back, normal, areaSubdivide, qfalse );
- FreeWinding( w );
- return;
- }
- }
- // create a light from this
- area = WindingArea (w);
- if ( area <= 0 || area > 20000000 ) {
- return;
- }
- numAreaLights++;
- dl = malloc(sizeof(*dl));
- memset (dl, 0, sizeof(*dl));
- dl->next = lights;
- lights = dl;
- dl->type = emit_area;
- WindingCenter( w, dl->origin );
- dl->w = w;
- VectorCopy ( normal, dl->normal);
- dl->dist = DotProduct( dl->origin, normal );
- value = ls->value;
- intensity = value * area * areaScale;
- VectorAdd( dl->origin, dl->normal, dl->origin );
- VectorCopy( ls->color, dl->color );
- dl->photons = intensity;
- // emitColor is irrespective of the area
- VectorScale( ls->color, value*formFactorValueScale*areaScale, dl->emitColor );
- dl->si = ls;
- if ( ls->contents & CONTENTS_FOG ) {
- dl->twosided = qtrue;
- }
- // optionally create a point backsplash light
- if ( backsplash && ls->backsplashFraction > 0 ) {
- dl2 = malloc(sizeof(*dl));
- memset (dl2, 0, sizeof(*dl2));
- dl2->next = lights;
- lights = dl2;
- dl2->type = emit_point;
- VectorMA( dl->origin, ls->backsplashDistance, normal, dl2->origin );
- VectorCopy( ls->color, dl2->color );
- dl2->photons = dl->photons * ls->backsplashFraction;
- dl2->si = ls;
- }
- }
- /*
- ===============
- CountLightmaps
- ===============
- */
- void CountLightmaps( void ) {
- int count;
- int i;
- dsurface_t *ds;
- qprintf ("--- CountLightmaps ---\n");
- count = 0;
- for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
- // see if this surface is light emiting
- ds = &drawSurfaces[i];
- if ( ds->lightmapNum > count ) {
- count = ds->lightmapNum;
- }
- }
- count++;
- numLightBytes = count * LIGHTMAP_WIDTH * LIGHTMAP_HEIGHT * 3;
- if ( numLightBytes > MAX_MAP_LIGHTING ) {
- Error("MAX_MAP_LIGHTING exceeded");
- }
- qprintf( "%5i drawSurfaces\n", numDrawSurfaces );
- qprintf( "%5i lightmaps\n", count );
- }
- /*
- ===============
- CreateSurfaceLights
- This creates area lights
- ===============
- */
- void CreateSurfaceLights( void ) {
- int i, j, side;
- dsurface_t *ds;
- shaderInfo_t *ls;
- winding_t *w;
- cFacet_t *f;
- light_t *dl;
- vec3_t origin;
- drawVert_t *dv;
- int c_lightSurfaces;
- float lightSubdivide;
- vec3_t normal;
- qprintf ("--- CreateSurfaceLights ---\n");
- c_lightSurfaces = 0;
- for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
- // see if this surface is light emiting
- ds = &drawSurfaces[i];
- ls = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
- if ( ls->value == 0 ) {
- continue;
- }
- // determine how much we need to chop up the surface
- if ( ls->lightSubdivide ) {
- lightSubdivide = ls->lightSubdivide;
- } else {
- lightSubdivide = defaultLightSubdivide;
- }
- c_lightSurfaces++;
- // an autosprite shader will become
- // a point light instead of an area light
- if ( ls->autosprite ) {
- // autosprite geometry should only have four vertexes
- if ( surfaceTest[i] ) {
- // curve or misc_model
- f = surfaceTest[i]->facets;
- if ( surfaceTest[i]->numFacets != 1 || f->numBoundaries != 4 ) {
- _printf( "WARNING: surface at (%i %i %i) has autosprite shader but isn't a quad\n",
- (int)f->points[0], (int)f->points[1], (int)f->points[2] );
- }
- VectorAdd( f->points[0], f->points[1], origin );
- VectorAdd( f->points[2], origin, origin );
- VectorAdd( f->points[3], origin, origin );
- VectorScale( origin, 0.25, origin );
- } else {
- // normal polygon
- dv = &drawVerts[ ds->firstVert ];
- if ( ds->numVerts != 4 ) {
- _printf( "WARNING: surface at (%i %i %i) has autosprite shader but %i verts\n",
- (int)dv->xyz[0], (int)dv->xyz[1], (int)dv->xyz[2] );
- continue;
- }
- VectorAdd( dv[0].xyz, dv[1].xyz, origin );
- VectorAdd( dv[2].xyz, origin, origin );
- VectorAdd( dv[3].xyz, origin, origin );
- VectorScale( origin, 0.25, origin );
- }
- numPointLights++;
- dl = malloc(sizeof(*dl));
- memset (dl, 0, sizeof(*dl));
- dl->next = lights;
- lights = dl;
- VectorCopy( origin, dl->origin );
- VectorCopy( ls->color, dl->color );
- dl->photons = ls->value * pointScale;
- dl->type = emit_point;
- continue;
- }
- // possibly create for both sides of the polygon
- for ( side = 0 ; side <= ls->twoSided ; side++ ) {
- // create area lights
- if ( surfaceTest[i] ) {
- // curve or misc_model
- for ( j = 0 ; j < surfaceTest[i]->numFacets ; j++ ) {
- f = surfaceTest[i]->facets + j;
- w = AllocWinding( f->numBoundaries );
- w->numpoints = f->numBoundaries;
- memcpy( w->p, f->points, f->numBoundaries * 12 );
- VectorCopy( f->surface, normal );
- if ( side ) {
- winding_t *t;
- t = w;
- w = ReverseWinding( t );
- FreeWinding( t );
- VectorSubtract( vec3_origin, normal, normal );
- }
- SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
- }
- } else {
- // normal polygon
- w = AllocWinding( ds->numVerts );
- w->numpoints = ds->numVerts;
- for ( j = 0 ; j < ds->numVerts ; j++ ) {
- VectorCopy( drawVerts[ds->firstVert+j].xyz, w->p[j] );
- }
- VectorCopy( ds->lightmapVecs[2], normal );
- if ( side ) {
- winding_t *t;
- t = w;
- w = ReverseWinding( t );
- FreeWinding( t );
- VectorSubtract( vec3_origin, normal, normal );
- }
- SubdivideAreaLight( ls, w, normal, lightSubdivide, qtrue );
- }
- }
- }
- _printf( "%5i light emitting surfaces\n", c_lightSurfaces );
- }
- /*
- ================
- FindSkyBrushes
- ================
- */
- void FindSkyBrushes( void ) {
- int i, j;
- dbrush_t *b;
- skyBrush_t *sb;
- shaderInfo_t *si;
- dbrushside_t *s;
- // find the brushes
- for ( i = 0 ; i < numbrushes ; i++ ) {
- b = &dbrushes[i];
- for ( j = 0 ; j < b->numSides ; j++ ) {
- s = &dbrushsides[ b->firstSide + j ];
- if ( dshaders[ s->shaderNum ].surfaceFlags & SURF_SKY ) {
- sb = &skyBrushes[ numSkyBrushes ];
- sb->b = b;
- sb->bounds[0][0] = -dplanes[ dbrushsides[ b->firstSide + 0 ].planeNum ].dist - 1;
- sb->bounds[1][0] = dplanes[ dbrushsides[ b->firstSide + 1 ].planeNum ].dist + 1;
- sb->bounds[0][1] = -dplanes[ dbrushsides[ b->firstSide + 2 ].planeNum ].dist - 1;
- sb->bounds[1][1] = dplanes[ dbrushsides[ b->firstSide + 3 ].planeNum ].dist + 1;
- sb->bounds[0][2] = -dplanes[ dbrushsides[ b->firstSide + 4 ].planeNum ].dist - 1;
- sb->bounds[1][2] = dplanes[ dbrushsides[ b->firstSide + 5 ].planeNum ].dist + 1;
- numSkyBrushes++;
- break;
- }
- }
- }
- // default
- VectorNormalize( sunDirection, sunDirection );
- // find the sky shader
- for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
- si = ShaderInfoForShader( dshaders[ drawSurfaces[i].shaderNum ].shader );
- if ( si->surfaceFlags & SURF_SKY ) {
- VectorCopy( si->sunLight, sunLight );
- VectorCopy( si->sunDirection, sunDirection );
- break;
- }
- }
- }
- /*
- =================================================================
- LIGHT SETUP
- =================================================================
- */
- /*
- ==================
- FindTargetEntity
- ==================
- */
- entity_t *FindTargetEntity( const char *target ) {
- int i;
- const char *n;
- for ( i = 0 ; i < num_entities ; i++ ) {
- n = ValueForKey (&entities[i], "targetname");
- if ( !strcmp (n, target) ) {
- return &entities[i];
- }
- }
- return NULL;
- }
- /*
- =============
- CreateEntityLights
- =============
- */
- void CreateEntityLights (void)
- {
- int i;
- light_t *dl;
- entity_t *e, *e2;
- const char *name;
- const char *target;
- vec3_t dest;
- const char *_color;
- float intensity;
- int spawnflags;
- //
- // entities
- //
- for ( i = 0 ; i < num_entities ; i++ ) {
- e = &entities[i];
- name = ValueForKey (e, "classname");
- if (strncmp (name, "light", 5))
- continue;
- numPointLights++;
- dl = malloc(sizeof(*dl));
- memset (dl, 0, sizeof(*dl));
- dl->next = lights;
- lights = dl;
- spawnflags = FloatForKey (e, "spawnflags");
- if ( spawnflags & 1 ) {
- dl->linearLight = qtrue;
- }
- GetVectorForKey (e, "origin", dl->origin);
- dl->style = FloatForKey (e, "_style");
- if (!dl->style)
- dl->style = FloatForKey (e, "style");
- if (dl->style < 0)
- dl->style = 0;
- intensity = FloatForKey (e, "light");
- if (!intensity)
- intensity = FloatForKey (e, "_light");
- if (!intensity)
- intensity = 300;
- _color = ValueForKey (e, "_color");
- if (_color && _color[0])
- {
- sscanf (_color, "%f %f %f", &dl->color[0],&dl->color[1],&dl->color[2]);
- ColorNormalize (dl->color, dl->color);
- }
- else
- dl->color[0] = dl->color[1] = dl->color[2] = 1.0;
- intensity = intensity * pointScale;
- dl->photons = intensity;
- dl->type = emit_point;
- // lights with a target will be spotlights
- target = ValueForKey (e, "target");
- if ( target[0] ) {
- float radius;
- float dist;
- e2 = FindTargetEntity (target);
- if (!e2) {
- _printf ("WARNING: light at (%i %i %i) has missing target\n",
- (int)dl->origin[0], (int)dl->origin[1], (int)dl->origin[2]);
- } else {
- GetVectorForKey (e2, "origin", dest);
- VectorSubtract (dest, dl->origin, dl->normal);
- dist = VectorNormalize (dl->normal, dl->normal);
- radius = FloatForKey (e, "radius");
- if ( !radius ) {
- radius = 64;
- }
- if ( !dist ) {
- dist = 64;
- }
- dl->radiusByDist = (radius + 16) / dist;
- dl->type = emit_spotlight;
- }
- }
- }
- }
- //=================================================================
- /*
- ================
- SetEntityOrigins
- Find the offset values for inline models
- ================
- */
- void SetEntityOrigins( void ) {
- int i, j;
- entity_t *e;
- vec3_t origin;
- const char *key;
- int modelnum;
- dmodel_t *dm;
- for ( i=0 ; i < num_entities ; i++ ) {
- e = &entities[i];
- key = ValueForKey (e, "model");
- if ( key[0] != '*' ) {
- continue;
- }
- modelnum = atoi( key + 1 );
- dm = &dmodels[ modelnum ];
- // set entity surface to true for all surfaces for this model
- for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
- entitySurface[ dm->firstSurface + j ] = qtrue;
- }
- key = ValueForKey (e, "origin");
- if ( !key[0] ) {
- continue;
- }
- GetVectorForKey ( e, "origin", origin );
- // set origin for all surfaces for this model
- for ( j = 0 ; j < dm->numSurfaces ; j++ ) {
- VectorCopy( origin, surfaceOrigin[ dm->firstSurface + j ] );
- }
- }
- }
- /*
- =================================================================
- =================================================================
- */
- #define MAX_POINTS_ON_WINDINGS 64
- /*
- ================
- PointToPolygonFormFactor
- ================
- */
- float PointToPolygonFormFactor( const vec3_t point, const vec3_t normal, const winding_t *w ) {
- vec3_t triVector, triNormal;
- int i, j;
- vec3_t dirs[MAX_POINTS_ON_WINDING];
- float total;
- float dot, angle, facing;
- for ( i = 0 ; i < w->numpoints ; i++ ) {
- VectorSubtract( w->p[i], point, dirs[i] );
- VectorNormalize( dirs[i], dirs[i] );
- }
- // duplicate first vertex to avoid mod operation
- VectorCopy( dirs[0], dirs[i] );
- total = 0;
- for ( i = 0 ; i < w->numpoints ; i++ ) {
- j = i+1;
- dot = DotProduct( dirs[i], dirs[j] );
- // roundoff can cause slight creep, which gives an IND from acos
- if ( dot > 1.0 ) {
- dot = 1.0;
- } else if ( dot < -1.0 ) {
- dot = -1.0;
- }
-
- angle = acos( dot );
- CrossProduct( dirs[i], dirs[j], triVector );
- if ( VectorNormalize( triVector, triNormal ) < 0.0001 ) {
- continue;
- }
- facing = DotProduct( normal, triNormal );
- total += facing * angle;
- if ( total > 6.3 || total < -6.3 ) {
- static qboolean printed;
- if ( !printed ) {
- printed = qtrue;
- _printf( "WARNING: bad PointToPolygonFormFactor: %f at %1.1f %1.1f %1.1f from %1.1f %1.1f %1.1f\n", total,
- w->p[i][0], w->p[i][1], w->p[i][2], point[0], point[1], point[2]);
- }
- return 0;
- }
- }
- total /= 2*3.141592657; // now in the range of 0 to 1 over the entire incoming hemisphere
- return total;
- }
- /*
- ================
- FilterTrace
- Returns 0 to 1.0 filter fractions for the given trace
- ================
- */
- void FilterTrace( const vec3_t start, const vec3_t end, vec3_t filter ) {
- float d1, d2;
- filter_t *f;
- int filterNum;
- vec3_t point;
- float frac;
- int i;
- float s, t;
- int u, v;
- int x, y;
- byte *pixel;
- float radius;
- float len;
- vec3_t total;
- filter[0] = 1.0;
- filter[1] = 1.0;
- filter[2] = 1.0;
- for ( filterNum = 0 ; filterNum < numFilters ; filterNum++ ) {
- f = &filters[ filterNum ];
- // see if the plane is crossed
- d1 = DotProduct( start, f->plane ) - f->plane[3];
- d2 = DotProduct( end, f->plane ) - f->plane[3];
- if ( ( d1 < 0 ) == ( d2 < 0 ) ) {
- continue;
- }
- // calculate the crossing point
- frac = d1 / ( d1 - d2 );
- for ( i = 0 ; i < 3 ; i++ ) {
- point[i] = start[i] + frac * ( end[i] - start[i] );
- }
- VectorSubtract( point, f->origin, point );
- s = DotProduct( point, f->vectors[0] );
- t = 1.0 - DotProduct( point, f->vectors[1] );
- if ( s < 0 || s >= 1.0 || t < 0 || t >= 1.0 ) {
- continue;
- }
- // decide the filter size
- radius = 10 * frac;
- len = VectorLength( f->vectors[0] );
- if ( !len ) {
- continue;
- }
- radius = radius * len * f->si->width;
- // look up the filter, taking multiple samples
- VectorClear( total );
- for ( u = -1 ; u <= 1 ; u++ ) {
- for ( v = -1 ; v <=1 ; v++ ) {
- x = s * f->si->width + u * radius;
- if ( x < 0 ) {
- x = 0;
- }
- if ( x >= f->si->width ) {
- x = f->si->width - 1;
- }
- y = t * f->si->height + v * radius;
- if ( y < 0 ) {
- y = 0;
- }
- if ( y >= f->si->height ) {
- y = f->si->height - 1;
- }
- pixel = f->si->pixels + ( y * f->si->width + x ) * 4;
- total[0] += pixel[0];
- total[1] += pixel[1];
- total[2] += pixel[2];
- }
- }
- filter[0] *= total[0]/(255.0*9);
- filter[1] *= total[1]/(255.0*9);
- filter[2] *= total[2]/(255.0*9);
- }
- }
- /*
- ================
- SunToPoint
- Returns an amount of light to add at the point
- ================
- */
- int c_sunHit, c_sunMiss;
- void SunToPoint( const vec3_t origin, traceWork_t *tw, vec3_t addLight ) {
- int i;
- trace_t trace;
- skyBrush_t *b;
- vec3_t end;
- if ( !numSkyBrushes ) {
- VectorClear( addLight );
- return;
- }
- VectorMA( origin, MAX_WORLD_COORD * 2, sunDirection, end );
- TraceLine( origin, end, &trace, qtrue, tw );
- // see if trace.hit is inside a sky brush
- for ( i = 0 ; i < numSkyBrushes ; i++) {
- b = &skyBrushes[ i ];
- // this assumes that sky brushes are axial...
- if ( trace.hit[0] < b->bounds[0][0]
- || trace.hit[0] > b->bounds[1][0]
- || trace.hit[1] < b->bounds[0][1]
- || trace.hit[1] > b->bounds[1][1]
- || trace.hit[2] < b->bounds[0][2]
- || trace.hit[2] > b->bounds[1][2] ) {
- continue;
- }
- // trace again to get intermediate filters
- TraceLine( origin, trace.hit, &trace, qtrue, tw );
- // we hit the sky, so add sunlight
- if ( numthreads == 1 ) {
- c_sunHit++;
- }
- addLight[0] = trace.filter[0] * sunLight[0];
- addLight[1] = trace.filter[1] * sunLight[1];
- addLight[2] = trace.filter[2] * sunLight[2];
- return;
- }
- if ( numthreads == 1 ) {
- c_sunMiss++;
- }
- VectorClear( addLight );
- }
- /*
- ================
- SunToPlane
- ================
- */
- void SunToPlane( const vec3_t origin, const vec3_t normal, vec3_t color, traceWork_t *tw ) {
- float angle;
- vec3_t sunColor;
- if ( !numSkyBrushes ) {
- return;
- }
- angle = DotProduct( normal, sunDirection );
- if ( angle <= 0 ) {
- return; // facing away
- }
- SunToPoint( origin, tw, sunColor );
- VectorMA( color, angle, sunColor, color );
- }
- /*
- ================
- LightingAtSample
- ================
- */
- void LightingAtSample( vec3_t origin, vec3_t normal, vec3_t color,
- qboolean testOcclusion, qboolean forceSunLight, traceWork_t *tw ) {
- light_t *light;
- trace_t trace;
- float angle;
- float add;
- float dist;
- vec3_t dir;
- VectorCopy( ambientColor, color );
- // trace to all the lights
- for ( light = lights ; light ; light = light->next ) {
- //MrE: if the light is behind the surface
- if ( DotProduct(light->origin, normal) - DotProduct(normal, origin) < 0 )
- continue;
- // testing exact PTPFF
- if ( exactPointToPolygon && light->type == emit_area ) {
- float factor;
- float d;
- vec3_t pushedOrigin;
- // see if the point is behind the light
- d = DotProduct( origin, light->normal ) - light->dist;
- if ( !light->twosided ) {
- if ( d < -1 ) {
- continue; // point is behind light
- }
- }
- // test occlusion and find light filters
- // clip the line, tracing from the surface towards the light
- if ( !notrace && testOcclusion ) {
- TraceLine( origin, light->origin, &trace, qfalse, tw );
- // other light rays must not hit anything
- if ( trace.passSolid ) {
- continue;
- }
- } else {
- trace.filter[0] = 1.0;
- trace.filter[1] = 1.0;
- trace.filter[2] = 1.0;
- }
- // nudge the point so that it is clearly forward of the light
- // so that surfaces meeting a light emiter don't get black edges
- if ( d > -8 && d < 8 ) {
- VectorMA( origin, (8-d), light->normal, pushedOrigin );
- } else {
- VectorCopy( origin, pushedOrigin );
- }
- // calculate the contribution
- factor = PointToPolygonFormFactor( pushedOrigin, normal, light->w );
- if ( factor <= 0 ) {
- if ( light->twosided ) {
- factor = -factor;
- } else {
- continue;
- }
- }
- color[0] += factor * light->emitColor[0] * trace.filter[0];
- color[1] += factor * light->emitColor[1] * trace.filter[1];
- color[2] += factor * light->emitColor[2] * trace.filter[2];
- continue;
- }
- // calculate the amount of light at this sample
- if ( light->type == emit_point ) {
- VectorSubtract( light->origin, origin, dir );
- dist = VectorNormalize( dir, dir );
- // clamp the distance to prevent super hot spots
- if ( dist < 16 ) {
- dist = 16;
- }
- angle = DotProduct( normal, dir );
- if ( light->linearLight ) {
- add = angle * light->photons * linearScale - dist;
- if ( add < 0 ) {
- add = 0;
- }
- } else {
- add = light->photons / ( dist * dist ) * angle;
- }
- } else if ( light->type == emit_spotlight ) {
- float distByNormal;
- vec3_t pointAtDist;
- float radiusAtDist;
- float sampleRadius;
- vec3_t distToSample;
- float coneScale;
- VectorSubtract( light->origin, origin, dir );
- distByNormal = -DotProduct( dir, light->normal );
- if ( distByNormal < 0 ) {
- continue;
- }
- VectorMA( light->origin, distByNormal, light->normal, pointAtDist );
- radiusAtDist = light->radiusByDist * distByNormal;
- VectorSubtract( origin, pointAtDist, distToSample );
- sampleRadius = VectorLength( distToSample );
- if ( sampleRadius >= radiusAtDist ) {
- continue; // outside the cone
- }
- if ( sampleRadius <= radiusAtDist - 32 ) {
- coneScale = 1.0; // fully inside
- } else {
- coneScale = ( radiusAtDist - sampleRadius ) / 32.0;
- }
-
- dist = VectorNormalize( dir, dir );
- // clamp the distance to prevent super hot spots
- if ( dist < 16 ) {
- dist = 16;
- }
- angle = DotProduct( normal, dir );
- add = light->photons / ( dist * dist ) * angle * coneScale;
- } else if ( light->type == emit_area ) {
- VectorSubtract( light->origin, origin, dir );
- dist = VectorNormalize( dir, dir );
- // clamp the distance to prevent super hot spots
- if ( dist < 16 ) {
- dist = 16;
- }
- angle = DotProduct( normal, dir );
- if ( angle <= 0 ) {
- continue;
- }
- angle *= -DotProduct( light->normal, dir );
- if ( angle <= 0 ) {
- continue;
- }
- if ( light->linearLight ) {
- add = angle * light->photons * linearScale - dist;
- if ( add < 0 ) {
- add = 0;
- }
- } else {
- add = light->photons / ( dist * dist ) * angle;
- }
- }
- if ( add <= 1.0 ) {
- continue;
- }
- // clip the line, tracing from the surface towards the light
- if ( !notrace && testOcclusion ) {
- TraceLine( origin, light->origin, &trace, qfalse, tw );
- // other light rays must not hit anything
- if ( trace.passSolid ) {
- continue;
- }
- } else {
- trace.filter[0] = 1;
- trace.filter[1] = 1;
- trace.filter[2] = 1;
- }
-
- // add the result
- color[0] += add * light->color[0] * trace.filter[0];
- color[1] += add * light->color[1] * trace.filter[1];
- color[2] += add * light->color[2] * trace.filter[2];
- }
- //
- // trace directly to the sun
- //
- if ( testOcclusion || forceSunLight ) {
- SunToPlane( origin, normal, color, tw );
- }
- }
- /*
- =============
- PrintOccluded
- For debugging
- =============
- */
- void PrintOccluded( byte occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE],
- int width, int height ) {
- int i, j;
- _printf( "\n" );
- for ( i = 0 ; i < height ; i++ ) {
- for ( j = 0 ; j < width ; j++ ) {
- _printf("%i", (int)occluded[j][i] );
- }
- _printf( "\n" );
- }
- }
- /*
- =============
- VertexLighting
- Vertex lighting will completely ignore occlusion, because
- shadows would not be resolvable anyway.
- =============
- */
- void VertexLighting( dsurface_t *ds, qboolean testOcclusion, qboolean forceSunLight, float scale, traceWork_t *tw ) {
- int i, j;
- drawVert_t *dv;
- vec3_t sample, normal;
- float max;
- VectorCopy( ds->lightmapVecs[2], normal );
- // generate vertex lighting
- for ( i = 0 ; i < ds->numVerts ; i++ ) {
- dv = &drawVerts[ ds->firstVert + i ];
- if ( ds->patchWidth ) {
- LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
- }
- else if (ds->surfaceType == MST_TRIANGLE_SOUP) {
- LightingAtSample( dv->xyz, dv->normal, sample, testOcclusion, forceSunLight, tw );
- }
- else {
- LightingAtSample( dv->xyz, normal, sample, testOcclusion, forceSunLight, tw );
- }
- if (scale >= 0)
- VectorScale(sample, scale, sample);
- // clamp with color normalization
- max = sample[0];
- if ( sample[1] > max ) {
- max = sample[1];
- }
- if ( sample[2] > max ) {
- max = sample[2];
- }
- if ( max > 255 ) {
- VectorScale( sample, 255/max, sample );
- }
- // save the sample
- for ( j = 0 ; j < 3 ; j++ ) {
- if ( sample[j] > 255 ) {
- sample[j] = 255;
- }
- dv->color[j] = sample[j];
- }
- // Don't bother writing alpha since it will already be set to 255,
- // plus we don't want to write over alpha generated by SetTerrainTextures
- //dv->color[3] = 255;
- }
- }
- /*
- =================
- LinearSubdivideMesh
- For extra lighting, just midpoint one of the axis.
- The edges are clamped at the original edges.
- =================
- */
- mesh_t *LinearSubdivideMesh( mesh_t *in ) {
- int i, j;
- mesh_t *out;
- drawVert_t *v1, *v2, *vout;
- out = malloc( sizeof( *out ) );
- out->width = in->width * 2;
- out->height = in->height;
- out->verts = malloc( out->width * out->height * sizeof(*out->verts) );
- for ( j = 0 ; j < in->height ; j++ ) {
- out->verts[ j * out->width + 0 ] = in->verts[ j * in->width + 0 ];
- out->verts[ j * out->width + out->width - 1 ] = in->verts[ j * in->width + in->width - 1 ];
- for ( i = 1 ; i < out->width - 1 ; i+= 2 ) {
- v1 = in->verts + j * in->width + (i >> 1);
- v2 = v1 + 1;
- vout = out->verts + j * out->width + i;
- vout->xyz[0] = 0.75 * v1->xyz[0] + 0.25 * v2->xyz[0];
- vout->xyz[1] = 0.75 * v1->xyz[1] + 0.25 * v2->xyz[1];
- vout->xyz[2] = 0.75 * v1->xyz[2] + 0.25 * v2->xyz[2];
- vout->normal[0] = 0.75 * v1->normal[0] + 0.25 * v2->normal[0];
- vout->normal[1] = 0.75 * v1->normal[1] + 0.25 * v2->normal[1];
- vout->normal[2] = 0.75 * v1->normal[2] + 0.25 * v2->normal[2];
- VectorNormalize( vout->normal, vout->normal );
- vout++;
- vout->xyz[0] = 0.25 * v1->xyz[0] + 0.75 * v2->xyz[0];
- vout->xyz[1] = 0.25 * v1->xyz[1] + 0.75 * v2->xyz[1];
- vout->xyz[2] = 0.25 * v1->xyz[2] + 0.75 * v2->xyz[2];
- vout->normal[0] = 0.25 * v1->normal[0] + 0.75 * v2->normal[0];
- vout->normal[1] = 0.25 * v1->normal[1] + 0.75 * v2->normal[1];
- vout->normal[2] = 0.25 * v1->normal[2] + 0.75 * v2->normal[2];
- VectorNormalize( vout->normal, vout->normal );
- }
- }
- FreeMesh( in );
- return out;
- }
- /*
- ==============
- ColorToBytes
- ==============
- */
- void ColorToBytes( const float *color, byte *colorBytes ) {
- float max;
- vec3_t sample;
- VectorCopy( color, sample );
- // clamp with color normalization
- max = sample[0];
- if ( sample[1] > max ) {
- max = sample[1];
- }
- if ( sample[2] > max ) {
- max = sample[2];
- }
- if ( max > 255 ) {
- VectorScale( sample, 255/max, sample );
- }
- colorBytes[ 0 ] = sample[0];
- colorBytes[ 1 ] = sample[1];
- colorBytes[ 2 ] = sample[2];
- }
- /*
- =============
- TraceLtm
- =============
- */
- void TraceLtm( int num ) {
- dsurface_t *ds;
- int i, j, k;
- int x, y;
- int position, numPositions;
- vec3_t base, origin, normal;
- byte occluded[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
- vec3_t color[LIGHTMAP_WIDTH*EXTRASCALE][LIGHTMAP_HEIGHT*EXTRASCALE];
- traceWork_t tw;
- vec3_t average;
- int count;
- mesh_t srcMesh, *mesh, *subdivided;
- shaderInfo_t *si;
- static float nudge[2][9] = {
- { 0, -1, 0, 1, -1, 1, -1, 0, 1 },
- { 0, -1, -1, -1, 0, 0, 1, 1, 1 }
- };
- int sampleWidth, sampleHeight, ssize;
- vec3_t lightmapOrigin, lightmapVecs[2];
- int widthtable[LIGHTMAP_WIDTH], heighttable[LIGHTMAP_WIDTH];
- ds = &drawSurfaces[num];
- si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
- // vertex-lit triangle model
- if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
- VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
- return;
- }
-
- if ( ds->lightmapNum == -1 ) {
- return; // doesn't need lighting at all
- }
- if (!novertexlighting) {
- // calculate the vertex lighting for gouraud shade mode
- VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
- }
- if ( ds->lightmapNum < 0 ) {
- return; // doesn't need lightmap lighting
- }
- si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
- ssize = samplesize;
- if (si->lightmapSampleSize)
- ssize = si->lightmapSampleSize;
- if (si->patchShadows)
- tw.patchshadows = qtrue;
- else
- tw.patchshadows = patchshadows;
- if ( ds->surfaceType == MST_PATCH ) {
- srcMesh.width = ds->patchWidth;
- srcMesh.height = ds->patchHeight;
- srcMesh.verts = drawVerts + ds->firstVert;
- mesh = SubdivideMesh( srcMesh, 8, 999 );
- PutMeshOnCurve( *mesh );
- MakeMeshNormals( *mesh );
- subdivided = RemoveLinearMeshColumnsRows( mesh );
- FreeMesh(mesh);
- mesh = SubdivideMeshQuads( subdivided, ssize, LIGHTMAP_WIDTH, widthtable, heighttable);
- if ( mesh->width != ds->lightmapWidth || mesh->height != ds->lightmapHeight ) {
- Error( "Mesh lightmap miscount");
- }
- if ( extra ) {
- mesh_t *mp;
- // chop it up for more light samples (leaking memory...)
- mp = mesh;//CopyMesh( mesh );
- mp = LinearSubdivideMesh( mp );
- mp = TransposeMesh( mp );
- mp = LinearSubdivideMesh( mp );
- mp = TransposeMesh( mp );
- mesh = mp;
- }
- } else {
- VectorCopy( ds->lightmapVecs[2], normal );
- if ( !extra ) {
- VectorCopy( ds->lightmapOrigin, lightmapOrigin );
- VectorCopy( ds->lightmapVecs[0], lightmapVecs[0] );
- VectorCopy( ds->lightmapVecs[1], lightmapVecs[1] );
- } else {
- // sample at a closer spacing for antialiasing
- VectorCopy( ds->lightmapOrigin, lightmapOrigin );
- VectorScale( ds->lightmapVecs[0], 0.5, lightmapVecs[0] );
- VectorScale( ds->lightmapVecs[1], 0.5, lightmapVecs[1] );
- VectorMA( lightmapOrigin, -0.5, lightmapVecs[0], lightmapOrigin );
- VectorMA( lightmapOrigin, -0.5, lightmapVecs[1], lightmapOrigin );
- }
- }
- if ( extra ) {
- sampleWidth = ds->lightmapWidth * 2;
- sampleHeight = ds->lightmapHeight * 2;
- } else {
- sampleWidth = ds->lightmapWidth;
- sampleHeight = ds->lightmapHeight;
- }
- memset ( color, 0, sizeof( color ) );
- // determine which samples are occluded
- memset ( occluded, 0, sizeof( occluded ) );
- for ( i = 0 ; i < sampleWidth ; i++ ) {
- for ( j = 0 ; j < sampleHeight ; j++ ) {
- if ( ds->patchWidth ) {
- numPositions = 9;
- VectorCopy( mesh->verts[j*mesh->width+i].normal, normal );
- // VectorNormalize( normal, normal );
- // push off of the curve a bit
- VectorMA( mesh->verts[j*mesh->width+i].xyz, 1, normal, base );
- MakeNormalVectors( normal, lightmapVecs[0], lightmapVecs[1] );
- } else {
- numPositions = 9;
- for ( k = 0 ; k < 3 ; k++ ) {
- base[k] = lightmapOrigin[k] + normal[k]
- + i * lightmapVecs[0][k]
- + j * lightmapVecs[1][k];
- }
- }
- VectorAdd( base, surfaceOrigin[ num ], base );
- // we may need to slightly nudge the sample point
- // if directly on a wall
- for ( position = 0 ; position < numPositions ; position++ ) {
- // calculate lightmap sample position
- for ( k = 0 ; k < 3 ; k++ ) {
- origin[k] = base[k] +
- + ( nudge[0][position]/16 ) * lightmapVecs[0][k]
- + ( nudge[1][position]/16 ) * lightmapVecs[1][k];
- }
- if ( notrace ) {
- break;
- }
- if ( !PointInSolid( origin ) ) {
- break;
- }
- }
- // if none of the nudges worked, this sample is occluded
- if ( position == numPositions ) {
- occluded[i][j] = qtrue;
- if ( numthreads == 1 ) {
- c_occluded++;
- }
- continue;
- }
-
- if ( numthreads == 1 ) {
- c_visible++;
- }
- occluded[i][j] = qfalse;
- LightingAtSample( origin, normal, color[i][j], qtrue, qfalse, &tw );
- }
- }
- if ( dump ) {
- PrintOccluded( occluded, sampleWidth, sampleHeight );
- }
- // calculate average values for occluded samples
- for ( i = 0 ; i < sampleWidth ; i++ ) {
- for ( j = 0 ; j < sampleHeight ; j++ ) {
- if ( !occluded[i][j] ) {
- continue;
- }
- // scan all surrounding samples
- count = 0;
- VectorClear( average );
- for ( x = -1 ; x <= 1; x++ ) {
- for ( y = -1 ; y <= 1 ; y++ ) {
- if ( i + x < 0 || i + x >= sampleWidth ) {
- continue;
- }
- if ( j + y < 0 || j + y >= sampleHeight ) {
- continue;
- }
- if ( occluded[i+x][j+y] ) {
- continue;
- }
- count++;
- VectorAdd( color[i+x][j+y], average, average );
- }
- }
- if ( count ) {
- VectorScale( average, 1.0/count, color[i][j] );
- }
- }
- }
- // average together the values if we are extra sampling
- if ( ds->lightmapWidth != sampleWidth ) {
- for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
- for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
- for ( k = 0 ; k < 3 ; k++ ) {
- float value, coverage;
- value = color[i*2][j*2][k] + color[i*2][j*2+1][k] +
- color[i*2+1][j*2][k] + color[i*2+1][j*2+1][k];
- coverage = 4;
- if ( extraWide ) {
- // wider than box filter
- if ( i > 0 ) {
- value += color[i*2-1][j*2][k] + color[i*2-1][j*2+1][k];
- value += color[i*2-2][j*2][k] + color[i*2-2][j*2+1][k];
- coverage += 4;
- }
- if ( i < ds->lightmapWidth - 1 ) {
- value += color[i*2+2][j*2][k] + color[i*2+2][j*2+1][k];
- value += color[i*2+3][j*2][k] + color[i*2+3][j*2+1][k];
- coverage += 4;
- }
- if ( j > 0 ) {
- value += color[i*2][j*2-1][k] + color[i*2+1][j*2-1][k];
- value += color[i*2][j*2-2][k] + color[i*2+1][j*2-2][k];
- coverage += 4;
- }
- if ( j < ds->lightmapHeight - 1 ) {
- value += color[i*2][j*2+2][k] + color[i*2+1][j*2+2][k];
- value += color[i*2][j*2+3][k] + color[i*2+1][j*2+3][k];
- coverage += 2;
- }
- }
- color[i][j][k] = value / coverage;
- }
- }
- }
- }
- // optionally create a debugging border around the lightmap
- if ( lightmapBorder ) {
- for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
- color[i][0][0] = 255;
- color[i][0][1] = 0;
- color[i][0][2] = 0;
- color[i][ds->lightmapHeight-1][0] = 255;
- color[i][ds->lightmapHeight-1][1] = 0;
- color[i][ds->lightmapHeight-1][2] = 0;
- }
- for ( i = 0 ; i < ds->lightmapHeight ; i++ ) {
- color[0][i][0] = 255;
- color[0][i][1] = 0;
- color[0][i][2] = 0;
- color[ds->lightmapWidth-1][i][0] = 255;
- color[ds->lightmapWidth-1][i][1] = 0;
- color[ds->lightmapWidth-1][i][2] = 0;
- }
- }
- // clamp the colors to bytes and store off
- for ( i = 0 ; i < ds->lightmapWidth ; i++ ) {
- for ( j = 0 ; j < ds->lightmapHeight ; j++ ) {
- k = ( ds->lightmapNum * LIGHTMAP_HEIGHT + ds->lightmapY + j)
- * LIGHTMAP_WIDTH + ds->lightmapX + i;
- ColorToBytes( color[i][j], lightBytes + k*3 );
- }
- }
- if (ds->surfaceType == MST_PATCH)
- {
- FreeMesh(mesh);
- }
- }
- //=============================================================================
- vec3_t gridMins;
- vec3_t gridSize = { 64, 64, 128 };
- int gridBounds[3];
- /*
- ========================
- LightContributionToPoint
- ========================
- */
- qboolean LightContributionToPoint( const light_t *light, const vec3_t origin,
- vec3_t color, traceWork_t *tw ) {
- trace_t trace;
- float add;
- add = 0;
- VectorClear( color );
- // testing exact PTPFF
- if ( exactPointToPolygon && light->type == emit_area ) {
- float factor;
- float d;
- vec3_t normal;
- // see if the point is behind the light
- d = DotProduct( origin, light->normal ) - light->dist;
- if ( !light->twosided ) {
- if ( d < 1 ) {
- return qfalse; // point is behind light
- }
- }
- // test occlusion
- // clip the line, tracing from the surface towards the light
- TraceLine( origin, light->origin, &trace, qfalse, tw );
- if ( trace.passSolid ) {
- return qfalse;
- }
- // calculate the contribution
- VectorSubtract( light->origin, origin, normal );
- if ( VectorNormalize( normal, normal ) == 0 ) {
- return qfalse;
- }
- factor = PointToPolygonFormFactor( origin, normal, light->w );
- if ( factor <= 0 ) {
- if ( light->twosided ) {
- factor = -factor;
- } else {
- return qfalse;
- }
- }
- VectorScale( light->emitColor, factor, color );
- return qtrue;
- }
- // calculate the amount of light at this sample
- if ( light->type == emit_point || light->type == emit_spotlight ) {
- vec3_t dir;
- float dist;
- VectorSubtract( light->origin, origin, dir );
- dist = VectorLength( dir );
- // clamp the distance to prevent super hot spots
- if ( dist < 16 ) {
- dist = 16;
- }
- if ( light->linearLight ) {
- add = light->photons * linearScale - dist;
- if ( add < 0 ) {
- add = 0;
- }
- } else {
- add = light->photons / ( dist * dist );
- }
- } else {
- return qfalse;
- }
- if ( add <= 1.0 ) {
- return qfalse;
- }
- // clip the line, tracing from the surface towards the light
- TraceLine( origin, light->origin, &trace, qfalse, tw );
- // other light rays must not hit anything
- if ( trace.passSolid ) {
- return qfalse;
- }
- // add the result
- color[0] = add * light->color[0];
- color[1] = add * light->color[1];
- color[2] = add * light->color[2];
- return qtrue;
- }
- typedef struct {
- vec3_t dir;
- vec3_t color;
- } contribution_t;
- /*
- =============
- TraceGrid
- Grid samples are foe quickly determining the lighting
- of dynamically placed entities in the world
- =============
- */
- #define MAX_CONTRIBUTIONS 1024
- void TraceGrid( int num ) {
- int x, y, z;
- vec3_t origin;
- light_t *light;
- vec3_t color;
- int mod;
- vec3_t directedColor;
- vec3_t summedDir;
- contribution_t contributions[MAX_CONTRIBUTIONS];
- int numCon;
- int i;
- traceWork_t tw;
- float addSize;
- mod = num;
- z = mod / ( gridBounds[0] * gridBounds[1] );
- mod -= z * ( gridBounds[0] * gridBounds[1] );
- y = mod / gridBounds[0];
- mod -= y * gridBounds[0];
- x = mod;
- origin[0] = gridMins[0] + x * gridSize[0];
- origin[1] = gridMins[1] + y * gridSize[1];
- origin[2] = gridMins[2] + z * gridSize[2];
- if ( PointInSolid( origin ) ) {
- vec3_t baseOrigin;
- int step;
- VectorCopy( origin, baseOrigin );
- // try to nudge the origin around to find a valid point
- for ( step = 9 ; step <= 18 ; step += 9 ) {
- for ( i = 0 ; i < 8 ; i++ ) {
- VectorCopy( baseOrigin, origin );
- if ( i & 1 ) {
- origin[0] += step;
- } else {
- origin[0] -= step;
- }
- if ( i & 2 ) {
- origin[1] += step;
- } else {
- origin[1] -= step;
- }
- if ( i & 4 ) {
- origin[2] += step;
- } else {
- origin[2] -= step;
- }
- if ( !PointInSolid( origin ) ) {
- break;
- }
- }
- if ( i != 8 ) {
- break;
- }
- }
- if ( step > 18 ) {
- // can't find a valid point at all
- for ( i = 0 ; i < 8 ; i++ ) {
- gridData[ num*8 + i ] = 0;
- }
- return;
- }
- }
- VectorClear( summedDir );
- // trace to all the lights
- // find the major light direction, and divide the
- // total light between that along the direction and
- // the remaining in the ambient
- numCon = 0;
- for ( light = lights ; light ; light = light->next ) {
- vec3_t add;
- vec3_t dir;
- float addSize;
- if ( !LightContributionToPoint( light, origin, add, &tw ) ) {
- continue;
- }
- VectorSubtract( light->origin, origin, dir );
- VectorNormalize( dir, dir );
- VectorCopy( add, contributions[numCon].color );
- VectorCopy( dir, contributions[numCon].dir );
- numCon++;
- addSize = VectorLength( add );
- VectorMA( summedDir, addSize, dir, summedDir );
- if ( numCon == MAX_CONTRIBUTIONS-1 ) {
- break;
- }
- }
- //
- // trace directly to the sun
- //
- SunToPoint( origin, &tw, color );
- addSize = VectorLength( color );
- if ( addSize > 0 ) {
- VectorCopy( color, contributions[numCon].color );
- VectorCopy( sunDirection, contributions[numCon].dir );
- VectorMA( summedDir, addSize, sunDirection, summedDir );
- numCon++;
- }
- // now that we have identified the primary light direction,
- // go back and seperate all the light into directed and ambient
- VectorNormalize( summedDir, summedDir );
- VectorCopy( ambientColor, color );
- VectorClear( directedColor );
- for ( i = 0 ; i < numCon ; i++ ) {
- float d;
- d = DotProduct( contributions[i].dir, summedDir );
- if ( d < 0 ) {
- d = 0;
- }
- VectorMA( directedColor, d, contributions[i].color, directedColor );
- // the ambient light will be at 1/4 the value of directed light
- d = 0.25 * ( 1.0 - d );
- VectorMA( color, d, contributions[i].color, color );
- }
- // now do some fudging to keep the ambient from being too low
- VectorMA( color, 0.25, directedColor, color );
- //
- // save the resulting value out
- //
- ColorToBytes( color, gridData + num*8 );
- ColorToBytes( directedColor, gridData + num*8 + 3 );
- VectorNormalize( summedDir, summedDir );
- NormalToLatLong( summedDir, gridData + num*8 + 6);
- }
- /*
- =============
- SetupGrid
- =============
- */
- void SetupGrid( void ) {
- int i;
- vec3_t maxs;
- for ( i = 0 ; i < 3 ; i++ ) {
- gridMins[i] = gridSize[i] * ceil( dmodels[0].mins[i] / gridSize[i] );
- maxs[i] = gridSize[i] * floor( dmodels[0].maxs[i] / gridSize[i] );
- gridBounds[i] = (maxs[i] - gridMins[i])/gridSize[i] + 1;
- }
- numGridPoints = gridBounds[0] * gridBounds[1] * gridBounds[2];
- if (numGridPoints * 8 >= MAX_MAP_LIGHTGRID)
- Error("MAX_MAP_LIGHTGRID");
- qprintf( "%5i gridPoints\n", numGridPoints );
- }
- //=============================================================================
- /*
- =============
- RemoveLightsInSolid
- =============
- */
- void RemoveLightsInSolid(void)
- {
- light_t *light, *prev;
- int numsolid = 0;
- prev = NULL;
- for ( light = lights ; light ; ) {
- if (PointInSolid(light->origin))
- {
- if (prev) prev->next = light->next;
- else lights = light->next;
- if (light->w)
- FreeWinding(light->w);
- free(light);
- numsolid++;
- if (prev)
- light = prev->next;
- else
- light = lights;
- }
- else
- {
- prev = light;
- light = light->next;
- }
- }
- _printf (" %7i lights in solid\n", numsolid);
- }
- /*
- =============
- LightWorld
- =============
- */
- void LightWorld (void) {
- float f;
- // determine the number of grid points
- SetupGrid();
- // find the optional world ambient
- GetVectorForKey( &entities[0], "_color", ambientColor );
- f = FloatForKey( &entities[0], "ambient" );
- VectorScale( ambientColor, f, ambientColor );
- // create lights out of patches and lights
- qprintf ("--- CreateLights ---\n");
- CreateEntityLights ();
- qprintf ("%i point lights\n", numPointLights);
- qprintf ("%i area lights\n", numAreaLights);
- if (!nogridlighting) {
- qprintf ("--- TraceGrid ---\n");
- RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
- qprintf( "%i x %i x %i = %i grid\n", gridBounds[0], gridBounds[1],
- gridBounds[2], numGridPoints);
- }
- qprintf ("--- TraceLtm ---\n");
- RunThreadsOnIndividual( numDrawSurfaces, qtrue, TraceLtm );
- qprintf( "%5i visible samples\n", c_visible );
- qprintf( "%5i occluded samples\n", c_occluded );
- }
- /*
- ========
- CreateFilters
- EXPERIMENTAL, UNUSED
- Look for transparent light filter surfaces.
- This will only work for flat 3*3 patches that exactly hold one copy of the texture.
- ========
- */
- #define PLANAR_PATCH_EPSILON 0.1
- void CreateFilters( void ) {
- int i;
- filter_t *f;
- dsurface_t *ds;
- shaderInfo_t *si;
- drawVert_t *v1, *v2, *v3;
- vec3_t d1, d2;
- int vertNum;
- numFilters = 0;
- return;
- for ( i = 0 ; i < numDrawSurfaces ; i++ ) {
- ds = &drawSurfaces[i];
- if ( !ds->patchWidth ) {
- continue;
- }
- si = ShaderInfoForShader( dshaders[ ds->shaderNum ].shader );
- /*
- if ( !(si->surfaceFlags & SURF_LIGHTFILTER) ) {
- continue;
- }
- */
- // we have a filter patch
- v1 = &drawVerts[ ds->firstVert ];
- if ( ds->patchWidth != 3 || ds->patchHeight != 3 ) {
- _printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't a 3 by 3\n",
- v1->xyz[0], v1->xyz[1], v1->xyz[2] );
- continue;
- }
- if ( numFilters == MAX_FILTERS ) {
- Error( "MAX_FILTERS" );
- }
- f = &filters[ numFilters ];
- numFilters++;
- v2 = &drawVerts[ ds->firstVert + 2 ];
- v3 = &drawVerts[ ds->firstVert + 6 ];
- VectorSubtract( v2->xyz, v1->xyz, d1 );
- VectorSubtract( v3->xyz, v1->xyz, d2 );
- VectorNormalize( d1, d1 );
- VectorNormalize( d2, d2 );
- CrossProduct( d1, d2, f->plane );
- f->plane[3] = DotProduct( v1->xyz, f->plane );
- // make sure all the control points are on the plane
- for ( vertNum = 0 ; vertNum < ds->numVerts ; vertNum++ ) {
- float d;
- d = DotProduct( drawVerts[ ds->firstVert + vertNum ].xyz, f->plane ) - f->plane[3];
- if ( fabs( d ) > PLANAR_PATCH_EPSILON ) {
- break;
- }
- }
- if ( vertNum != ds->numVerts ) {
- numFilters--;
- _printf("WARNING: patch at %i %i %i has SURF_LIGHTFILTER but isn't flat\n",
- v1->xyz[0], v1->xyz[1], v1->xyz[2] );
- continue;
- }
- }
- f = &filters[0];
- numFilters = 1;
- f->plane[0] = 1;
- f->plane[1] = 0;
- f->plane[2] = 0;
- f->plane[3] = 448;
- f->origin[0] = 448;
- f->origin[1] = 192;
- f->origin[2] = 0;
- f->vectors[0][0] = 0;
- f->vectors[0][1] = -1.0 / 128;
- f->vectors[0][2] = 0;
- f->vectors[1][0] = 0;
- f->vectors[1][1] = 0;
- f->vectors[1][2] = 1.0 / 128;
- f->si = ShaderInfoForShader( "textures/hell/blocks11ct" );
- }
- /*
- =============
- VertexLightingThread
- =============
- */
- void VertexLightingThread(int num) {
- dsurface_t *ds;
- traceWork_t tw;
- shaderInfo_t *si;
- ds = &drawSurfaces[num];
- // vertex-lit triangle model
- if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
- return;
- }
- if (novertexlighting)
- return;
- if ( ds->lightmapNum == -1 ) {
- return; // doesn't need lighting at all
- }
- si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
- // calculate the vertex lighting for gouraud shade mode
- VertexLighting( ds, si->vertexShadows, si->forceSunLight, si->vertexScale, &tw );
- }
- /*
- =============
- TriSoupLightingThread
- =============
- */
- void TriSoupLightingThread(int num) {
- dsurface_t *ds;
- traceWork_t tw;
- shaderInfo_t *si;
- ds = &drawSurfaces[num];
- si = ShaderInfoForShader( dshaders[ ds->shaderNum].shader );
- // vertex-lit triangle model
- if ( ds->surfaceType == MST_TRIANGLE_SOUP ) {
- VertexLighting( ds, !si->noVertexShadows, si->forceSunLight, 1.0, &tw );
- }
- }
- /*
- =============
- GridAndVertexLighting
- =============
- */
- void GridAndVertexLighting(void) {
- SetupGrid();
- FindSkyBrushes();
- CreateFilters();
- InitTrace();
- CreateEntityLights ();
- CreateSurfaceLights();
- if (!nogridlighting) {
- _printf ("--- TraceGrid ---\n");
- RunThreadsOnIndividual( numGridPoints, qtrue, TraceGrid );
- }
- if (!novertexlighting) {
- _printf ("--- Vertex Lighting ---\n");
- RunThreadsOnIndividual( numDrawSurfaces, qtrue, VertexLightingThread );
- }
- _printf("--- Model Lighting ---\n");
- RunThreadsOnIndividual( numDrawSurfaces, qtrue, TriSoupLightingThread );
- }
- /*
- ========
- LightMain
- ========
- */
- int LightMain (int argc, char **argv) {
- int i;
- double start, end;
- const char *value;
- _printf ("----- Lighting ----\n");
- verbose = qfalse;
- for (i=1 ; i<argc ; i++) {
- if (!strcmp(argv[i],"-tempname"))
- {
- i++;
- } else if (!strcmp(argv[i],"-v")) {
- verbose = qtrue;
- } else if (!strcmp(argv[i],"-threads")) {
- numthreads = atoi (argv[i+1]);
- i++;
- } else if (!strcmp(argv[i],"-area")) {
- areaScale *= atof(argv[i+1]);
- _printf ("area light scaling at %f\n", areaScale);
- i++;
- } else if (!strcmp(argv[i],"-point")) {
- pointScale *= atof(argv[i+1]);
- _printf ("point light scaling at %f\n", pointScale);
- i++;
- } else if (!strcmp(argv[i],"-notrace")) {
- notrace = qtrue;
- _printf ("No occlusion tracing\n");
- } else if (!strcmp(argv[i],"-patchshadows")) {
- patchshadows = qtrue;
- _printf ("Patch shadow casting enabled\n");
- } else if (!strcmp(argv[i],"-extra")) {
- extra = qtrue;
- _printf ("Extra detail tracing\n");
- } else if (!strcmp(argv[i],"-extrawide")) {
- extra = qtrue;
- extraWide = qtrue;
- _printf ("Extra wide detail tracing\n");
- } else if (!strcmp(argv[i], "-samplesize")) {
- samplesize = atoi(argv[i+1]);
- if (samplesize < 1) samplesize = 1;
- i++;
- _printf("lightmap sample size is %dx%d units\n", samplesize, samplesize);
- } else if (!strcmp(argv[i], "-novertex")) {
- novertexlighting = qtrue;
- _printf("no vertex lighting = true\n");
- } else if (!strcmp(argv[i], "-nogrid")) {
- nogridlighting = qtrue;
- _printf("no grid lighting = true\n");
- } else if (!strcmp(argv[i],"-border")) {
- lightmapBorder = qtrue;
- _printf ("Adding debug border to lightmaps\n");
- } else if (!strcmp(argv[i],"-nosurf")) {
- noSurfaces = qtrue;
- _printf ("Not tracing against surfaces\n" );
- } else if (!strcmp(argv[i],"-dump")) {
- dump = qtrue;
- _printf ("Dumping occlusion maps\n");
- } else {
- break;
- }
- }
- ThreadSetDefault ();
- if (i != argc - 1) {
- _printf("usage: q3map -light [-<switch> [-<switch> ...]] <mapname>\n"
- "\n"
- "Switches:\n"
- " v = verbose output\n"
- " threads <X> = set number of threads to X\n"
- " area <V> = set the area light scale to V\n"
- " point <W> = set the point light scale to W\n"
- " notrace = don't cast any shadows\n"
- " extra = enable super sampling for anti-aliasing\n"
- " extrawide = same as extra but smoothen more\n"
- " nogrid = don't calculate light grid for dynamic model lighting\n"
- " novertex = don't calculate vertex lighting\n"
- " samplesize <N> = set the lightmap pixel size to NxN units\n");
- exit(0);
- }
- start = I_FloatTime ();
- SetQdirFromPath (argv[i]);
- #ifdef _WIN32
- InitPakFile(gamedir, NULL);
- #endif
- strcpy (source, ExpandArg(argv[i]));
- StripExtension (source);
- DefaultExtension (source, ".bsp");
- LoadShaderInfo();
- _printf ("reading %s\n", source);
- LoadBSPFile (source);
- FindSkyBrushes();
- ParseEntities();
- value = ValueForKey( &entities[0], "gridsize" );
- if (strlen(value)) {
- sscanf( value, "%f %f %f", &gridSize[0], &gridSize[1], &gridSize[2] );
- _printf("grid size = {%1.1f, %1.1f, %1.1f}\n", gridSize[0], gridSize[1], gridSize[2]);
- }
- CreateFilters();
- InitTrace();
- SetEntityOrigins();
- CountLightmaps();
- CreateSurfaceLights();
- LightWorld();
- _printf ("writing %s\n", source);
- WriteBSPFile (source);
- end = I_FloatTime ();
- _printf ("%5.0f seconds elapsed\n", end-start);
-
- return 0;
- }
|