--- /dev/null
+/*
+===========================================================================
+Copyright (C) 1997-2006 Id Software, Inc.
+
+This file is part of Quake 2 Tools source code.
+
+Quake 2 Tools 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 2 Tools 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 Quake 2 Tools source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+===========================================================================
+*/
+
+#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 *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 = 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 ();
+
+ qprintf ("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);
+ qprintf ("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 main (int argc, char **argv)
+{
+ int i;
+ double start, end;
+ char name[1024];
+
+ printf ("----- Radiosity ----\n");
+
+ verbose = false;
+
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!strcmp(argv[i],"-dump"))
+ dumppatches = true;
+ else if (!strcmp(argv[i],"-bounce"))
+ {
+ numbounce = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-v"))
+ {
+ verbose = true;
+ }
+ else if (!strcmp(argv[i],"-extra"))
+ {
+ extrasamples = true;
+ printf ("extrasamples = true\n");
+ }
+ else if (!strcmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-chop"))
+ {
+ subdiv = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-scale"))
+ {
+ lightscale = atof (argv[i+1]);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-direct"))
+ {
+ direct_scale *= atof(argv[i+1]);
+ printf ("direct light scaling at %f\n", direct_scale);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-entity"))
+ {
+ entity_scale *= atof(argv[i+1]);
+ printf ("entity light scaling at %f\n", entity_scale);
+ i++;
+ }
+ else if (!strcmp(argv[i],"-glview"))
+ {
+ glview = true;
+ printf ("glview = true\n");
+ }
+ else if (!strcmp(argv[i],"-nopvs"))
+ {
+ nopvs = true;
+ printf ("nopvs = true\n");
+ }
+ else if (!strcmp(argv[i],"-ambient"))
+ {
+ ambient = atof (argv[i+1]) * 128;
+ i++;
+ }
+ else if (!strcmp(argv[i],"-maxlight"))
+ {
+ maxlight = atof (argv[i+1]) * 128;
+ i++;
+ }
+ else if (!strcmp (argv[i],"-tmpin"))
+ strcpy (inbase, "/tmp");
+ else if (!strcmp (argv[i],"-tmpout"))
+ strcpy (outbase, "/tmp");
+ else
+ break;
+ }
+
+ ThreadSetDefault ();
+
+ if (maxlight > 255)
+ maxlight = 255;
+
+ if (i != argc - 1)
+ Error ("usage: qrad [-v] [-chop num] [-scale num] [-ambient num] [-maxlight num] [-threads num] bspfile");
+
+ start = I_FloatTime ();
+
+ SetQdirFromPath (argv[i]);
+ strcpy (source, ExpandArg(argv[i]));
+ StripExtension (source);
+ DefaultExtension (source, ".bsp");
+
+// ReadLightFile ();
+
+ sprintf (name, "%s%s", inbase, source);
+ printf ("reading %s\n", name);
+ LoadBSPFile (name);
+ ParseEntities ();
+ CalcTextureReflectivity ();
+
+ if (!visdatasize)
+ {
+ printf ("No vis information, direct lighting only.\n");
+ numbounce = 0;
+ ambient = 0.1;
+ }
+
+ RadWorld ();
+
+ sprintf (name, "%s%s", outbase, source);
+ printf ("writing %s\n", name);
+ WriteBSPFile (name);
+
+ end = I_FloatTime ();
+ printf ("%5.0f seconds elapsed\n", end-start);
+
+ return 0;
+}
+