2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
32 every surface must be divided into at least two patches each axis
\r
36 patch_t *face_patches[MAX_MAP_FACES];
\r
37 entity_t *face_entity[MAX_MAP_FACES];
\r
38 patch_t patches[MAX_PATCHES];
\r
39 unsigned num_patches;
\r
41 vec3_t radiosity[MAX_PATCHES]; // light leaving a patch
\r
42 vec3_t illumination[MAX_PATCHES]; // light arriving at a patch
\r
44 vec3_t face_offset[MAX_MAP_FACES]; // for rotating bmodels
\r
45 dplane_t backplanes[MAX_MAP_PLANES];
\r
47 char inbase[32], outbase[32];
\r
49 int fakeplanes; // created planes for origin offset
\r
52 qboolean extrasamples;
\r
55 qboolean dumppatches;
\r
57 void BuildLightmaps (void);
\r
58 int TestLine (vec3_t start, vec3_t stop);
\r
63 float maxlight = 196;
\r
65 float lightscale = 1.0;
\r
73 float direct_scale = 0.4;
\r
74 float entity_scale = 1.0;
\r
77 ===================================================================
\r
81 ===================================================================
\r
90 void MakeBackplanes (void)
\r
94 for (i=0 ; i<numplanes ; i++)
\r
96 backplanes[i].dist = -dplanes[i].dist;
\r
97 VectorSubtract (vec3_origin, dplanes[i].normal, backplanes[i].normal);
\r
101 int leafparents[MAX_MAP_LEAFS];
\r
102 int nodeparents[MAX_MAP_NODES];
\r
109 void MakeParents (int nodenum, int parent)
\r
114 nodeparents[nodenum] = parent;
\r
115 node = &dnodes[nodenum];
\r
117 for (i=0 ; i<2 ; i++)
\r
119 j = node->children[i];
\r
121 leafparents[-j - 1] = nodenum;
\r
123 MakeParents (j, nodenum);
\r
129 ===================================================================
\r
133 ===================================================================
\r
136 int PointInLeafnum (vec3_t point)
\r
144 while (nodenum >= 0)
\r
146 node = &dnodes[nodenum];
\r
147 plane = &dplanes[node->planenum];
\r
148 dist = DotProduct (point, plane->normal) - plane->dist;
\r
150 nodenum = node->children[0];
\r
152 nodenum = node->children[1];
\r
155 return -nodenum - 1;
\r
159 dleaf_t *Rad_PointInLeaf (vec3_t point)
\r
163 num = PointInLeafnum (point);
\r
164 return &dleafs[num];
\r
168 qboolean PvsForOrigin (vec3_t org, byte *pvs)
\r
174 memset (pvs, 255, (numleafs+7)/8 );
\r
178 leaf = Rad_PointInLeaf (org);
\r
179 if (leaf->cluster == -1)
\r
180 return false; // in solid leaf
\r
182 DecompressVis (dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs);
\r
193 int total_transfer;
\r
195 void MakeTransfers (int i)
\r
202 patch_t *patch, *patch2;
\r
206 float transfers[MAX_PATCHES], *all_transfers;
\r
209 byte pvs[(MAX_MAP_LEAFS+7)/8];
\r
212 patch = patches + i;
\r
215 VectorCopy (patch->origin, origin);
\r
216 plane = *patch->plane;
\r
218 if (!PvsForOrigin (patch->origin, pvs))
\r
221 // find out which patch2s will collect light
\r
224 all_transfers = transfers;
\r
225 patch->numtransfers = 0;
\r
226 for (j=0, patch2 = patches ; j<num_patches ; j++, patch2++)
\r
236 cluster = patch2->cluster;
\r
239 if ( ! ( pvs[cluster>>3] & (1<<(cluster&7)) ) )
\r
240 continue; // not in pvs
\r
243 // calculate vector
\r
244 VectorSubtract (patch2->origin, origin, delta);
\r
245 dist = VectorNormalize (delta, delta);
\r
247 continue; // should never happen
\r
250 scale = DotProduct (delta, plane.normal);
\r
251 scale *= -DotProduct (delta, patch2->plane->normal);
\r
255 // check exact tramsfer
\r
256 if (TestLine_r (0, patch->origin, patch2->origin) )
\r
259 trans = scale * patch2->area / (dist*dist);
\r
262 trans = 0; // rounding errors...
\r
264 transfers[j] = trans;
\r
268 patch->numtransfers++;
\r
272 // copy the transfers out and normalize
\r
273 // total should be somewhere near PI if everything went right
\r
274 // because partial occlusion isn't accounted for, and nearby
\r
275 // patches have underestimated form factors, it will usually
\r
276 // be higher than PI
\r
277 if (patch->numtransfers)
\r
281 if (patch->numtransfers < 0 || patch->numtransfers > MAX_PATCHES)
\r
282 Error ("Weird numtransfers");
\r
283 s = patch->numtransfers * sizeof(transfer_t);
\r
284 patch->transfers = malloc (s);
\r
285 if (!patch->transfers)
\r
286 Error ("Memory allocation failure");
\r
289 // normalize all transfers so all of the light
\r
290 // is transfered to the surroundings
\r
292 t = patch->transfers;
\r
294 for (j=0 ; j<num_patches ; j++)
\r
296 if (transfers[j] <= 0)
\r
298 itrans = transfers[j]*0x10000 / total;
\r
300 t->transfer = itrans;
\r
306 // don't bother locking around this. not that important.
\r
307 total_transfer += patch->numtransfers;
\r
316 void FreeTransfers (void)
\r
320 for (i=0 ; i<num_patches ; i++)
\r
322 free (patches[i].transfers);
\r
323 patches[i].transfers = NULL;
\r
328 //===================================================================
\r
335 void WriteWorld (char *name)
\r
342 out = fopen (name, "w");
\r
344 Error ("Couldn't open %s", name);
\r
346 for (j=0, patch=patches ; j<num_patches ; j++, patch++)
\r
348 w = patch->winding;
\r
349 fprintf (out, "%i\n", w->numpoints);
\r
350 for (i=0 ; i<w->numpoints ; i++)
\r
352 fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
\r
356 patch->totallight[0],
\r
357 patch->totallight[1],
\r
358 patch->totallight[2]);
\r
360 fprintf (out, "\n");
\r
371 void WriteGlView (void)
\r
379 strcpy (name, source);
\r
380 StripExtension (name);
\r
381 strcat (name, ".glr");
\r
383 f = fopen (name, "w");
\r
385 Error ("Couldn't open %s", f);
\r
387 for (j=0 ; j<num_patches ; j++)
\r
391 fprintf (f, "%i\n", w->numpoints);
\r
392 for (i=0 ; i<w->numpoints ; i++)
\r
394 fprintf (f, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",
\r
398 p->totallight[0]/128,
\r
399 p->totallight[1]/128,
\r
400 p->totallight[2]/128);
\r
409 //==============================================================
\r
416 float CollectLight (void)
\r
424 for (i=0, patch=patches ; i<num_patches ; i++, patch++)
\r
426 // skys never collect light, it is just dropped
\r
429 VectorClear (radiosity[i]);
\r
430 VectorClear (illumination[i]);
\r
434 for (j=0 ; j<3 ; j++)
\r
436 patch->totallight[j] += illumination[i][j] / patch->area;
\r
437 radiosity[i][j] = illumination[i][j] * patch->reflectivity[j];
\r
440 total += radiosity[i][0] + radiosity[i][1] + radiosity[i][2];
\r
441 VectorClear (illumination[i]);
\r
452 Send light out to other patches
\r
456 void ShootLight (int patchnum)
\r
464 // this is the amount of light we are distributing
\r
465 // prescale it so that multiplying by the 16 bit
\r
466 // transfer values gives a proper output value
\r
467 for (k=0 ; k<3 ; k++)
\r
468 send[k] = radiosity[patchnum][k] / 0x10000;
\r
469 patch = &patches[patchnum];
\r
471 trans = patch->transfers;
\r
472 num = patch->numtransfers;
\r
474 for (k=0 ; k<num ; k++, trans++)
\r
476 for (l=0 ; l<3 ; l++)
\r
477 illumination[trans->patch][l] += send[l]*trans->transfer;
\r
486 void BounceLight (void)
\r
493 for (i=0 ; i<num_patches ; i++)
\r
496 for (j=0 ; j<3 ; j++)
\r
498 // p->totallight[j] = p->samplelight[j];
\r
499 radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area;
\r
503 for (i=0 ; i<numbounce ; i++)
\r
505 RunThreadsOnIndividual (num_patches, false, ShootLight);
\r
506 added = CollectLight ();
\r
508 Sys_FPrintf( SYS_VRB, "bounce:%i added:%f\n", i, added);
\r
509 if ( dumppatches && (i==0 || i == numbounce-1) )
\r
511 sprintf (name, "bounce%i.txt", i);
\r
519 //==============================================================
\r
521 void CheckPatches (void)
\r
526 for (i=0 ; i<num_patches ; i++)
\r
528 patch = &patches[i];
\r
529 if (patch->totallight[0] < 0 || patch->totallight[1] < 0 || patch->totallight[2] < 0)
\r
530 Error ("negative patch totallight\n");
\r
539 void RadWorld (void)
\r
541 if (numnodes == 0 || numfaces == 0)
\r
542 Error ("Empty map");
\r
544 MakeParents (0, -1);
\r
545 MakeTnodes (&dmodels[0]);
\r
547 // turn each face into a single patch
\r
550 // subdivide patches to a maximum dimension
\r
551 SubdividePatches ();
\r
553 // create directlights out of patches and lights
\r
554 CreateDirectLights ();
\r
556 // build initial facelights
\r
557 RunThreadsOnIndividual (numfaces, true, BuildFacelights);
\r
561 // build transfer lists
\r
562 RunThreadsOnIndividual (num_patches, true, MakeTransfers);
\r
563 Sys_FPrintf( SYS_VRB, "transfer lists: %5.1f megs\n"
\r
564 , (float)total_transfer * sizeof(transfer_t) / (1024*1024));
\r
566 // spread light around
\r
577 // blend bounced light into direct light and save
\r
582 RunThreadsOnIndividual (numfaces, true, FinalLightFace);
\r
597 int total_rad_time;
\r
599 Sys_Printf ("\n----- RAD ----\n\n");
\r
601 if (maxlight > 255)
\r
604 start = I_FloatTime ();
\r
606 if ( !strcmp( game, "heretic2" ) )
\r
607 CalcTextureReflectivity = &CalcTextureReflectivity_Heretic2;
\r
609 CalcTextureReflectivity = &CalcTextureReflectivity_Quake2;
\r
611 SetQdirFromPath (mapname);
\r
612 strcpy (source, ExpandArg(mapname));
\r
613 StripExtension (source);
\r
614 DefaultExtension (source, ".bsp");
\r
616 // ReadLightFile ();
\r
618 sprintf (name, "%s%s", inbase, source);
\r
619 Sys_Printf ("reading %s\n", name);
\r
620 LoadBSPFile (name);
\r
622 (*CalcTextureReflectivity) ();
\r
626 Sys_Printf ("No vis information, direct lighting only.\n");
\r
633 sprintf (name, "%s%s", outbase, source);
\r
634 Sys_Printf ("writing %s\n", name);
\r
635 WriteBSPFile (name);
\r
637 end = I_FloatTime ();
\r
638 total_rad_time = (int) (end-start);
\r
639 Sys_Printf("\nRAD Time: ");
\r
640 if ( total_rad_time > 59 )
\r
641 Sys_Printf("%d Minutes ", total_rad_time/60 );
\r
642 Sys_Printf( "%d Seconds\n", total_rad_time%60 );
\r