]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - tools/quake2/q2map/qrad.c
transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / tools / quake2 / q2map / qrad.c
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \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
11 \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
16 \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
20 */\r
21 // qrad.c\r
22 \r
23 #include "qrad.h"\r
24 \r
25 \r
26 \r
27 /*\r
28 \r
29 NOTES\r
30 -----\r
31 \r
32 every surface must be divided into at least two patches each axis\r
33 \r
34 */\r
35 \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
40 \r
41 vec3_t          radiosity[MAX_PATCHES];         // light leaving a patch\r
42 vec3_t          illumination[MAX_PATCHES];      // light arriving at a patch\r
43 \r
44 vec3_t          face_offset[MAX_MAP_FACES];             // for rotating bmodels\r
45 dplane_t        backplanes[MAX_MAP_PLANES];\r
46 \r
47 char            inbase[32], outbase[32];\r
48 \r
49 int                     fakeplanes;                                     // created planes for origin offset \r
50 \r
51 int             numbounce = 8;\r
52 qboolean        extrasamples;\r
53 \r
54 float   subdiv = 64;\r
55 qboolean        dumppatches;\r
56 \r
57 void BuildLightmaps (void);\r
58 int TestLine (vec3_t start, vec3_t stop);\r
59 \r
60 int             junk;\r
61 \r
62 float   ambient = 0;\r
63 float   maxlight = 196;\r
64 \r
65 float   lightscale = 1.0;\r
66 \r
67 qboolean        glview;\r
68 \r
69 qboolean        nopvs;\r
70 \r
71 char            source[1024];\r
72 \r
73 float   direct_scale =  0.4;\r
74 float   entity_scale =  1.0;\r
75 \r
76 /*\r
77 ===================================================================\r
78 \r
79 MISC\r
80 \r
81 ===================================================================\r
82 */\r
83 \r
84 \r
85 /*\r
86 =============\r
87 MakeBackplanes\r
88 =============\r
89 */\r
90 void MakeBackplanes (void)\r
91 {\r
92         int             i;\r
93 \r
94         for (i=0 ; i<numplanes ; i++)\r
95         {\r
96                 backplanes[i].dist = -dplanes[i].dist;\r
97                 VectorSubtract (vec3_origin, dplanes[i].normal, backplanes[i].normal);\r
98         }\r
99 }\r
100 \r
101 int             leafparents[MAX_MAP_LEAFS];\r
102 int             nodeparents[MAX_MAP_NODES];\r
103 \r
104 /*\r
105 =============\r
106 MakeParents\r
107 =============\r
108 */\r
109 void MakeParents (int nodenum, int parent)\r
110 {\r
111         int             i, j;\r
112         dnode_t *node;\r
113 \r
114         nodeparents[nodenum] = parent;\r
115         node = &dnodes[nodenum];\r
116 \r
117         for (i=0 ; i<2 ; i++)\r
118         {\r
119                 j = node->children[i];\r
120                 if (j < 0)\r
121                         leafparents[-j - 1] = nodenum;\r
122                 else\r
123                         MakeParents (j, nodenum);\r
124         }\r
125 }\r
126 \r
127 \r
128 /*\r
129 ===================================================================\r
130 \r
131 TRANSFER SCALES\r
132 \r
133 ===================================================================\r
134 */\r
135 \r
136 int     PointInLeafnum (vec3_t point)\r
137 {\r
138         int             nodenum;\r
139         vec_t   dist;\r
140         dnode_t *node;\r
141         dplane_t        *plane;\r
142 \r
143         nodenum = 0;\r
144         while (nodenum >= 0)\r
145         {\r
146                 node = &dnodes[nodenum];\r
147                 plane = &dplanes[node->planenum];\r
148                 dist = DotProduct (point, plane->normal) - plane->dist;\r
149                 if (dist > 0)\r
150                         nodenum = node->children[0];\r
151                 else\r
152                         nodenum = node->children[1];\r
153         }\r
154 \r
155         return -nodenum - 1;\r
156 }\r
157 \r
158 \r
159 dleaf_t         *Rad_PointInLeaf (vec3_t point)\r
160 {\r
161         int             num;\r
162 \r
163         num = PointInLeafnum (point);\r
164         return &dleafs[num];\r
165 }\r
166 \r
167 \r
168 qboolean PvsForOrigin (vec3_t org, byte *pvs)\r
169 {\r
170         dleaf_t *leaf;\r
171 \r
172         if (!visdatasize)\r
173         {\r
174                 memset (pvs, 255, (numleafs+7)/8 );\r
175                 return true;\r
176         }\r
177 \r
178         leaf = Rad_PointInLeaf (org);\r
179         if (leaf->cluster == -1)\r
180                 return false;           // in solid leaf\r
181 \r
182         DecompressVis (dvisdata + dvis->bitofs[leaf->cluster][DVIS_PVS], pvs);\r
183         return true;\r
184 }\r
185 \r
186 \r
187 /*\r
188 =============\r
189 MakeTransfers\r
190 \r
191 =============\r
192 */\r
193 int     total_transfer;\r
194 \r
195 void MakeTransfers (int i)\r
196 {\r
197         int                     j;\r
198         vec3_t          delta;\r
199         vec_t           dist, scale;\r
200         float           trans;\r
201         int                     itrans;\r
202         patch_t         *patch, *patch2;\r
203         float           total;\r
204         dplane_t        plane;\r
205         vec3_t          origin;\r
206         float           transfers[MAX_PATCHES], *all_transfers;\r
207         int                     s;\r
208         int                     itotal;\r
209         byte            pvs[(MAX_MAP_LEAFS+7)/8];\r
210         int                     cluster;\r
211 \r
212         patch = patches + i;\r
213         total = 0;\r
214 \r
215         VectorCopy (patch->origin, origin);\r
216         plane = *patch->plane;\r
217 \r
218         if (!PvsForOrigin (patch->origin, pvs))\r
219                 return;\r
220 \r
221         // find out which patch2s will collect light\r
222         // from patch\r
223 \r
224         all_transfers = transfers;\r
225         patch->numtransfers = 0;\r
226         for (j=0, patch2 = patches ; j<num_patches ; j++, patch2++)\r
227         {\r
228                 transfers[j] = 0;\r
229 \r
230                 if (j == i)\r
231                         continue;\r
232 \r
233                 // check pvs bit\r
234                 if (!nopvs)\r
235                 {\r
236                         cluster = patch2->cluster;\r
237                         if (cluster == -1)\r
238                                 continue;\r
239                         if ( ! ( pvs[cluster>>3] & (1<<(cluster&7)) ) )\r
240                                 continue;               // not in pvs\r
241                 }\r
242 \r
243                 // calculate vector\r
244                 VectorSubtract (patch2->origin, origin, delta);\r
245                 dist = VectorNormalize (delta, delta);\r
246                 if (!dist)\r
247                         continue;       // should never happen\r
248 \r
249                 // reletive angles\r
250                 scale = DotProduct (delta, plane.normal);\r
251                 scale *= -DotProduct (delta, patch2->plane->normal);\r
252                 if (scale <= 0)\r
253                         continue;\r
254 \r
255                 // check exact tramsfer\r
256                 if (TestLine_r (0, patch->origin, patch2->origin) )\r
257                         continue;\r
258 \r
259                 trans = scale * patch2->area / (dist*dist);\r
260 \r
261                 if (trans < 0)\r
262                         trans = 0;              // rounding errors...\r
263 \r
264                 transfers[j] = trans;\r
265                 if (trans > 0)\r
266                 {\r
267                         total += trans;\r
268                         patch->numtransfers++;\r
269                 }\r
270         }\r
271 \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
278         {\r
279                 transfer_t      *t;\r
280                 \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
287 \r
288                 //\r
289                 // normalize all transfers so all of the light\r
290                 // is transfered to the surroundings\r
291                 //\r
292                 t = patch->transfers;\r
293                 itotal = 0;\r
294                 for (j=0 ; j<num_patches ; j++)\r
295                 {\r
296                         if (transfers[j] <= 0)\r
297                                 continue;\r
298                         itrans = transfers[j]*0x10000 / total;\r
299                         itotal += itrans;\r
300                         t->transfer = itrans;\r
301                         t->patch = j;\r
302                         t++;\r
303                 }\r
304         }\r
305 \r
306         // don't bother locking around this.  not that important.\r
307         total_transfer += patch->numtransfers;\r
308 }\r
309 \r
310 \r
311 /*\r
312 =============\r
313 FreeTransfers\r
314 =============\r
315 */\r
316 void FreeTransfers (void)\r
317 {\r
318         int             i;\r
319 \r
320         for (i=0 ; i<num_patches ; i++)\r
321         {\r
322                 free (patches[i].transfers);\r
323                 patches[i].transfers = NULL;\r
324         }\r
325 }\r
326 \r
327 \r
328 //===================================================================\r
329 \r
330 /*\r
331 =============\r
332 WriteWorld\r
333 =============\r
334 */\r
335 void WriteWorld (char *name)\r
336 {\r
337         int             i, j;\r
338         FILE            *out;\r
339         patch_t         *patch;\r
340         winding_t       *w;\r
341 \r
342         out = fopen (name, "w");\r
343         if (!out)\r
344                 Error ("Couldn't open %s", name);\r
345 \r
346         for (j=0, patch=patches ; j<num_patches ; j++, patch++)\r
347         {\r
348                 w = patch->winding;\r
349                 fprintf (out, "%i\n", w->numpoints);\r
350                 for (i=0 ; i<w->numpoints ; i++)\r
351                 {\r
352                         fprintf (out, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",\r
353                                 w->p[i][0],\r
354                                 w->p[i][1],\r
355                                 w->p[i][2],\r
356                                 patch->totallight[0],\r
357                                 patch->totallight[1],\r
358                                 patch->totallight[2]);\r
359                 }\r
360                 fprintf (out, "\n");\r
361         }\r
362 \r
363         fclose (out);\r
364 }\r
365 \r
366 /*\r
367 =============\r
368 WriteGlView\r
369 =============\r
370 */\r
371 void WriteGlView (void)\r
372 {\r
373         char    name[1024];\r
374         FILE    *f;\r
375         int             i, j;\r
376         patch_t *p;\r
377         winding_t       *w;\r
378 \r
379         strcpy (name, source);\r
380         StripExtension (name);\r
381         strcat (name, ".glr");\r
382 \r
383         f = fopen (name, "w");\r
384         if (!f)\r
385                 Error ("Couldn't open %s", f);\r
386 \r
387         for (j=0 ; j<num_patches ; j++)\r
388         {\r
389                 p = &patches[j];\r
390                 w = p->winding;\r
391                 fprintf (f, "%i\n", w->numpoints);\r
392                 for (i=0 ; i<w->numpoints ; i++)\r
393                 {\r
394                         fprintf (f, "%5.2f %5.2f %5.2f %5.3f %5.3f %5.3f\n",\r
395                                 w->p[i][0],\r
396                                 w->p[i][1],\r
397                                 w->p[i][2],\r
398                                 p->totallight[0]/128,\r
399                                 p->totallight[1]/128,\r
400                                 p->totallight[2]/128);\r
401                 }\r
402                 fprintf (f, "\n");\r
403         }\r
404 \r
405         fclose (f);\r
406 }\r
407 \r
408 \r
409 //==============================================================\r
410 \r
411 /*\r
412 =============\r
413 CollectLight\r
414 =============\r
415 */\r
416 float CollectLight (void)\r
417 {\r
418         int             i, j;\r
419         patch_t *patch;\r
420         vec_t   total;\r
421 \r
422         total = 0;\r
423 \r
424         for (i=0, patch=patches ; i<num_patches ; i++, patch++)\r
425         {\r
426                 // skys never collect light, it is just dropped\r
427                 if (patch->sky)\r
428                 {\r
429                         VectorClear (radiosity[i]);\r
430                         VectorClear (illumination[i]);\r
431                         continue;\r
432                 }\r
433 \r
434                 for (j=0 ; j<3 ; j++)\r
435                 {\r
436                         patch->totallight[j] += illumination[i][j] / patch->area;\r
437                         radiosity[i][j] = illumination[i][j] * patch->reflectivity[j];\r
438                 }\r
439 \r
440                 total += radiosity[i][0] + radiosity[i][1] + radiosity[i][2];\r
441                 VectorClear (illumination[i]);\r
442         }\r
443 \r
444         return total;\r
445 }\r
446 \r
447 \r
448 /*\r
449 =============\r
450 ShootLight\r
451 \r
452 Send light out to other patches\r
453   Run multi-threaded\r
454 =============\r
455 */\r
456 void ShootLight (int patchnum)\r
457 {\r
458         int                     k, l;\r
459         transfer_t      *trans;\r
460         int                     num;\r
461         patch_t         *patch;\r
462         vec3_t          send;\r
463 \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
470 \r
471         trans = patch->transfers;\r
472         num = patch->numtransfers;\r
473 \r
474         for (k=0 ; k<num ; k++, trans++)\r
475         {\r
476                 for (l=0 ; l<3 ; l++)\r
477                         illumination[trans->patch][l] += send[l]*trans->transfer;\r
478         }\r
479 }\r
480 \r
481 /*\r
482 =============\r
483 BounceLight\r
484 =============\r
485 */\r
486 void BounceLight (void)\r
487 {\r
488         int             i, j;\r
489         float   added;\r
490         char    name[64];\r
491         patch_t *p;\r
492 \r
493         for (i=0 ; i<num_patches ; i++)\r
494         {\r
495                 p = &patches[i];\r
496                 for (j=0 ; j<3 ; j++)\r
497                 {\r
498 //                      p->totallight[j] = p->samplelight[j];\r
499                         radiosity[i][j] = p->samplelight[j] * p->reflectivity[j] * p->area;\r
500                 }\r
501         }\r
502 \r
503         for (i=0 ; i<numbounce ; i++)\r
504         {\r
505                 RunThreadsOnIndividual (num_patches, false, ShootLight);\r
506                 added = CollectLight ();\r
507 \r
508                 Sys_FPrintf( SYS_VRB, "bounce:%i added:%f\n", i, added);\r
509                 if ( dumppatches && (i==0 || i == numbounce-1) )\r
510                 {\r
511                         sprintf (name, "bounce%i.txt", i);\r
512                         WriteWorld (name);\r
513                 }\r
514         }\r
515 }\r
516 \r
517 \r
518 \r
519 //==============================================================\r
520 \r
521 void CheckPatches (void)\r
522 {\r
523         int             i;\r
524         patch_t *patch;\r
525 \r
526         for (i=0 ; i<num_patches ; i++)\r
527         {\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
531         }\r
532 }\r
533 \r
534 /*\r
535 =============\r
536 RadWorld\r
537 =============\r
538 */\r
539 void RadWorld (void)\r
540 {\r
541         if (numnodes == 0 || numfaces == 0)\r
542                 Error ("Empty map");\r
543         MakeBackplanes ();\r
544         MakeParents (0, -1);\r
545         MakeTnodes (&dmodels[0]);\r
546 \r
547         // turn each face into a single patch\r
548         MakePatches ();\r
549 \r
550         // subdivide patches to a maximum dimension\r
551         SubdividePatches ();\r
552 \r
553         // create directlights out of patches and lights\r
554         CreateDirectLights ();\r
555 \r
556         // build initial facelights\r
557         RunThreadsOnIndividual (numfaces, true, BuildFacelights);\r
558 \r
559         if (numbounce > 0)\r
560         {\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
565 \r
566                 // spread light around\r
567                 BounceLight ();\r
568                 \r
569                 FreeTransfers ();\r
570 \r
571                 CheckPatches ();\r
572         }\r
573 \r
574         if (glview)\r
575                 WriteGlView ();\r
576 \r
577         // blend bounced light into direct light and save\r
578         PairEdges ();\r
579         LinkPlaneFaces ();\r
580 \r
581         lightdatasize = 0;\r
582         RunThreadsOnIndividual (numfaces, true, FinalLightFace);\r
583 }\r
584 \r
585 \r
586 /*\r
587 ========\r
588 main\r
589 \r
590 light modelfile\r
591 ========\r
592 */\r
593 int RAD_Main ()\r
594 {\r
595         double          start, end;\r
596         char            name[1024];\r
597         int             total_rad_time;\r
598 \r
599         Sys_Printf ("\n----- RAD ----\n\n");\r
600 \r
601         if (maxlight > 255)\r
602                 maxlight = 255;\r
603 \r
604         start = I_FloatTime ();\r
605 \r
606         if ( !strcmp( game, "heretic2" ) )\r
607                 CalcTextureReflectivity = &CalcTextureReflectivity_Heretic2;\r
608         else\r
609                 CalcTextureReflectivity = &CalcTextureReflectivity_Quake2;\r
610                 \r
611         SetQdirFromPath (mapname);      \r
612         strcpy (source, ExpandArg(mapname));\r
613         StripExtension (source);\r
614         DefaultExtension (source, ".bsp");\r
615 \r
616 //      ReadLightFile ();\r
617 \r
618         sprintf (name, "%s%s", inbase, source);\r
619         Sys_Printf ("reading %s\n", name);\r
620         LoadBSPFile (name);\r
621         ParseEntities ();\r
622         (*CalcTextureReflectivity) ();\r
623 \r
624         if (!visdatasize)\r
625         {\r
626                 Sys_Printf ("No vis information, direct lighting only.\n");\r
627                 numbounce = 0;\r
628                 ambient = 0.1;\r
629         }\r
630 \r
631         RadWorld ();\r
632 \r
633         sprintf (name, "%s%s", outbase, source);\r
634         Sys_Printf ("writing %s\n", name);\r
635         WriteBSPFile (name);\r
636 \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
643         \r
644         \r
645         return 0;\r
646 }\r
647 \r