/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // qrad.c #include "qrad.h" /* NOTES ----- every surface must be divided into at least two patches each axis */ patch_t *face_patches[MAX_MAP_FACES]; entity_t *face_entity[MAX_MAP_FACES]; patch_t patches[MAX_PATCHES]; unsigned num_patches; vec3_t radiosity[MAX_PATCHES]; // light leaving a patch vec3_t illumination[MAX_PATCHES]; // light arriving at a patch vec3_t face_offset[MAX_MAP_FACES]; // for rotating bmodels dplane_t backplanes[MAX_MAP_PLANES]; char inbase[32], outbase[32]; int fakeplanes; // created planes for origin offset int numbounce = 8; qboolean extrasamples; float subdiv = 64; qboolean dumppatches; void BuildLightmaps (void); int TestLine (vec3_t start, vec3_t stop); int junk; float ambient = 0; float maxlight = 196; float lightscale = 1.0; qboolean glview; qboolean nopvs; char source[1024]; float direct_scale = 0.4; float entity_scale = 1.0; /* =================================================================== MISC =================================================================== */ /* ============= MakeBackplanes ============= */ void MakeBackplanes (void) { int i; for (i=0 ; ichildren[i]; if (j < 0) leafparents[-j - 1] = nodenum; else MakeParents (j, nodenum); } } /* =================================================================== TRANSFER SCALES =================================================================== */ int PointInLeafnum (vec3_t point) { int nodenum; vec_t dist; dnode_t *node; dplane_t *plane; nodenum = 0; while (nodenum >= 0) { node = &dnodes[nodenum]; plane = &dplanes[node->planenum]; dist = DotProduct (point, plane->normal) - plane->dist; if (dist > 0) nodenum = node->children[0]; else nodenum = node->children[1]; } return -nodenum - 1; } dleaf_t *Rad_PointInLeaf (vec3_t point) { int num; num = PointInLeafnum (point); return &dleafs[num]; } qboolean PvsForOrigin (vec3_t org, byte *pvs) { dleaf_t *leaf; if (!visdatasize) { memset (pvs, 255, (numleafs+7)/8 ); return true; } leaf = Rad_PointInLeaf (org); if (leaf->cluster == -1) return false; // in solid leaf DecompressVis (dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs); return true; } /* ============= MakeTransfers ============= */ int total_transfer; void MakeTransfers (int i) { int j; vec3_t delta; vec_t dist, scale; float trans; int itrans; patch_t *patch, *patch2; float total; dplane_t plane; vec3_t origin; float transfers[MAX_PATCHES], *all_transfers; int s; int itotal; byte pvs[(MAX_MAP_LEAFS+7)/8]; int cluster; patch = patches + i; total = 0; VectorCopy (patch->origin, origin); plane = *patch->plane; if (!PvsForOrigin (patch->origin, pvs)) return; // find out which patch2s will collect light // from patch all_transfers = transfers; patch->numtransfers = 0; for (j=0, patch2 = patches ; jcluster; if (cluster == -1) continue; if ( ! ( pvs[cluster>>3] & (1<<(cluster&7)) ) ) continue; // not in pvs } // calculate vector VectorSubtract (patch2->origin, origin, delta); dist = VectorNormalize (delta, delta); if (!dist) continue; // should never happen // reletive angles scale = DotProduct (delta, plane.normal); scale *= -DotProduct (delta, patch2->plane->normal); if (scale <= 0) continue; // check exact tramsfer if (TestLine_r (0, patch->origin, patch2->origin) ) continue; trans = scale * patch2->area / (dist*dist); if (trans < 0) trans = 0; // rounding errors... transfers[j] = trans; if (trans > 0) { total += trans; patch->numtransfers++; } } // copy the transfers out and normalize // total should be somewhere near PI if everything went right // because partial occlusion isn't accounted for, and nearby // patches have underestimated form factors, it will usually // be higher than PI if (patch->numtransfers) { transfer_t *t; if (patch->numtransfers < 0 || patch->numtransfers > MAX_PATCHES) Error ("Weird numtransfers"); s = patch->numtransfers * sizeof(transfer_t); patch->transfers = malloc (s); if (!patch->transfers) Error ("Memory allocation failure"); // // normalize all transfers so all of the light // is transfered to the surroundings // t = patch->transfers; itotal = 0; for (j=0 ; jtransfer = itrans; t->patch = j; t++; } } // don't bother locking around this. not that important. total_transfer += patch->numtransfers; } /* ============= FreeTransfers ============= */ void FreeTransfers (void) { int i; for (i=0 ; iwinding; fprintf (out, "%i\n", w->numpoints); for (i=0 ; inumpoints ; i++) { fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", w->p[i][0], w->p[i][1], w->p[i][2], patch->totallight[0], patch->totallight[1], patch->totallight[2]); } fprintf (out, "\n"); } fclose (out); } /* ============= WriteGlView ============= */ void WriteGlView (void) { char name[1024]; FILE *f; int i, j; patch_t *p; winding_t *w; strcpy (name, source); StripExtension (name); strcat (name, ".glr"); f = fopen (name, "w"); if (!f) Error ("Couldn't open %s", f); for (j=0 ; jwinding; fprintf (f, "%i\n", w->numpoints); for (i=0 ; inumpoints ; i++) { fprintf (f, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n", w->p[i][0], w->p[i][1], w->p[i][2], p->totallight[0]/128, p->totallight[1]/128, p->totallight[2]/128); } fprintf (f, "\n"); } fclose (f); } //============================================================== /* ============= CollectLight ============= */ float CollectLight (void) { int i, j; patch_t *patch; vec_t total; total = 0; for (i=0, patch=patches ; isky) { VectorClear (radiosity[i]); VectorClear (illumination[i]); continue; } for (j=0 ; j<3 ; j++) { patch->totallight[j] += illumination[i][j] / patch->area; radiosity[i][j] = illumination[i][j] * patch->reflectivity[j]; } total += radiosity[i][0] + radiosity[i][1] + radiosity[i][2]; VectorClear (illumination[i]); } return total; } /* ============= ShootLight Send light out to other patches Run multi-threaded ============= */ void ShootLight (int patchnum) { int k, l; transfer_t *trans; int num; patch_t *patch; vec3_t send; // this is the amount of light we are distributing // prescale it so that multiplying by the 16 bit // transfer values gives a proper output value for (k=0 ; k<3 ; k++) send[k] = radiosity[patchnum][k] / 0x10000; patch = &patches[patchnum]; trans = patch->transfers; num = patch->numtransfers; for (k=0 ; kpatch][l] += send[l]*trans->transfer; } } /* ============= BounceLight ============= */ void BounceLight (void) { int i, j; float added; char name[64]; patch_t *p; for (i=0 ; itotallight[j] = p->samplelight[j]; radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area; } } for (i=0 ; itotallight[0] < 0 || patch->totallight[1] < 0 || patch->totallight[2] < 0) Error ("negative patch totallight\n"); } } /* ============= RadWorld ============= */ void RadWorld (void) { if (numnodes == 0 || numfaces == 0) Error ("Empty map"); MakeBackplanes (); MakeParents (0, -1); MakeTnodes (&dmodels[0]); // turn each face into a single patch MakePatches (); // subdivide patches to a maximum dimension SubdividePatches (); // create directlights out of patches and lights CreateDirectLights (); // build initial facelights RunThreadsOnIndividual (numfaces, true, BuildFacelights); if (numbounce > 0) { // build transfer lists RunThreadsOnIndividual (num_patches, true, MakeTransfers); Sys_FPrintf( SYS_VRB, "transfer lists: %5.1f megs\n" , (float)total_transfer * sizeof(transfer_t) / (1024*1024)); // spread light around BounceLight (); FreeTransfers (); CheckPatches (); } if (glview) WriteGlView (); // blend bounced light into direct light and save PairEdges (); LinkPlaneFaces (); lightdatasize = 0; RunThreadsOnIndividual (numfaces, true, FinalLightFace); } /* ======== main light modelfile ======== */ int RAD_Main () { double start, end; char name[1024]; int total_rad_time; Sys_Printf ("\n----- RAD ----\n\n"); if (maxlight > 255) maxlight = 255; start = I_FloatTime (); if ( !strcmp( game, "heretic2" ) ) CalcTextureReflectivity = &CalcTextureReflectivity_Heretic2; else CalcTextureReflectivity = &CalcTextureReflectivity_Quake2; SetQdirFromPath (mapname); strcpy (source, ExpandArg(mapname)); StripExtension (source); DefaultExtension (source, ".bsp"); // ReadLightFile (); sprintf (name, "%s%s", inbase, source); Sys_Printf ("reading %s\n", name); LoadBSPFile (name); ParseEntities (); (*CalcTextureReflectivity) (); if (!visdatasize) { Sys_Printf ("No vis information, direct lighting only.\n"); numbounce = 0; ambient = 0.1; } RadWorld (); sprintf (name, "%s%s", outbase, source); Sys_Printf ("writing %s\n", name); WriteBSPFile (name); end = I_FloatTime (); total_rad_time = (int) (end-start); Sys_Printf("\nRAD Time: "); if ( total_rad_time > 59 ) Sys_Printf("%d Minutes ", total_rad_time/60 ); Sys_Printf( "%d Seconds\n", total_rad_time%60 ); return 0; }