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