]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - tools/quake2/q2map/qrad.c
Merge branch 'msys2thread' into 'master'
[xonotic/netradiant.git] / tools / quake2 / q2map / qrad.c
index e324c5745222b212d3bb468143c96a88a34a5181..c17b5bb9e01cd47b632f72dad55b7fc90adf7c6e 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;
+}