1 /*
2
3 BINARY SPACE PARTITION -aka- B S P
4
5 Code based on original code from Valve Software,
6 Modified by Sean "Zoner" Cavanaugh (seanc@gearboxsoftware.com) with permission.
7 Modified by Tony "Merl" Moore (merlinis@bigpond.net.au) [AJM]
8
9 */
10
11 #ifdef SYSTEM_WIN32
12 #define WIN32_LEAN_AND_MEAN
13 #include <windows.h>
14 #endif
15
16 #include "bsp5.h"
17
18 /*
19
20 NOTES
21
22
23 */
24
25 static FILE* polyfiles[NUM_HULLS];
26 int g_hullnum = 0;
27
28 static face_t* validfaces[MAX_INTERNAL_MAP_PLANES];
29
30 char g_bspfilename[_MAX_PATH];
31 char g_pointfilename[_MAX_PATH];
32 char g_linefilename[_MAX_PATH];
33 char g_portfilename[_MAX_PATH];
34
35 // command line flags
36 bool g_noopt = DEFAULT_NOOPT; // don't optimize BSP on write
37 bool g_nofill = DEFAULT_NOFILL; // dont fill "-nofill"
38 bool g_notjunc = DEFAULT_NOTJUNC;
39 bool g_noclip = DEFAULT_NOCLIP; // no clipping hull "-noclip"
40 bool g_chart = DEFAULT_CHART; // print out chart? "-chart"
41 bool g_estimate = DEFAULT_ESTIMATE; // estimate mode "-estimate"
42 bool g_info = DEFAULT_INFO;
43 bool g_bLeakOnly = DEFAULT_LEAKONLY; // leakonly mode "-leakonly"
44 bool g_bLeaked = false;
45 int g_subdivide_size = DEFAULT_SUBDIVIDE_SIZE;
46
47 #ifdef ZHLT_NULLTEX // AJM
48 bool g_bUseNullTex = DEFAULT_NULLTEX; // "-nonulltex"
49 #endif
50
51 #ifdef ZHLT_DETAIL // AJM
52 bool g_bDetailBrushes = DEFAULT_DETAIL; // "-nodetail"
53 #endif
54
55 #ifdef ZHLT_PROGRESSFILE // AJM
56 char* g_progressfile = DEFAULT_PROGRESSFILE; // "-progressfile path"
57 #endif
58
59 #ifdef ZHLT_INFO_COMPILE_PARAMETERS// AJM
60 // =====================================================================================
61 // GetParamsFromEnt
62 // this function is called from parseentity when it encounters the
63 // info_compile_parameters entity. each tool should have its own version of this
64 // to handle its own specific settings.
65 // =====================================================================================
GetParamsFromEnt(entity_t * mapent)66 void GetParamsFromEnt(entity_t* mapent)
67 {
68 int iTmp;
69
70 Log("\nCompile Settings detected from info_compile_parameters entity\n");
71
72 // verbose(choices) : "Verbose compile messages" : 0 = [ 0 : "Off" 1 : "On" ]
73 iTmp = IntForKey(mapent, "verbose");
74 if (iTmp == 1)
75 {
76 g_verbose = true;
77 }
78 else if (iTmp == 0)
79 {
80 g_verbose = false;
81 }
82 Log("%30s [ %-9s ]\n", "Compile Option", "setting");
83 Log("%30s [ %-9s ]\n", "Verbose Compile Messages", g_verbose ? "on" : "off");
84
85 // estimate(choices) :"Estimate Compile Times?" : 0 = [ 0: "Yes" 1: "No" ]
86 if (IntForKey(mapent, "estimate"))
87 {
88 g_estimate = true;
89 }
90 else
91 {
92 g_estimate = false;
93 }
94 Log("%30s [ %-9s ]\n", "Estimate Compile Times", g_estimate ? "on" : "off");
95
96 // priority(choices) : "Priority Level" : 0 = [ 0 : "Normal" 1 : "High" -1 : "Low" ]
97 if (!strcmp(ValueForKey(mapent, "priority"), "1"))
98 {
99 g_threadpriority = eThreadPriorityHigh;
100 Log("%30s [ %-9s ]\n", "Thread Priority", "high");
101 }
102 else if (!strcmp(ValueForKey(mapent, "priority"), "-1"))
103 {
104 g_threadpriority = eThreadPriorityLow;
105 Log("%30s [ %-9s ]\n", "Thread Priority", "low");
106 }
107
108 /*
109 hlbsp(choices) : "HLBSP" : 0 =
110 [
111 0 : "Off"
112 1 : "Normal"
113 2 : "Leakonly"
114 ]
115 */
116 iTmp = IntForKey(mapent, "hlbsp");
117 if (iTmp == 0)
118 {
119 Fatal(assume_TOOL_CANCEL,
120 "%s flag was not checked in info_compile_parameters entity, execution of %s cancelled", g_Program, g_Program);
121 CheckFatal();
122 }
123 else if (iTmp == 1)
124 {
125 g_bLeakOnly = false;
126 }
127 else if (iTmp == 2)
128 {
129 g_bLeakOnly = true;
130 }
131 Log("%30s [ %-9s ]\n", "Leakonly Mode", g_bLeakOnly ? "on" : "off");
132
133 iTmp = IntForKey(mapent, "noopt");
134 if(iTmp == 0)
135 {
136 g_noopt = false;
137 }
138 else
139 {
140 g_noopt = true;
141 }
142
143 /*
144 nocliphull(choices) : "Generate clipping hulls" : 0 =
145 [
146 0 : "Yes"
147 1 : "No"
148 ]
149 */
150 iTmp = IntForKey(mapent, "nocliphull");
151 if (iTmp == 0)
152 {
153 g_noclip = false;
154 }
155 else if (iTmp == 1)
156 {
157 g_noclip = true;
158 }
159 Log("%30s [ %-9s ]\n", "Clipping Hull Generation", g_noclip ? "off" : "on");
160
161 //////////////////
162 Verbose("\n");
163 }
164 #endif
165
166 // =====================================================================================
167 // Extract File stuff (ExtractFile | ExtractFilePath | ExtractFileBase)
168 //
169 // With VS 2005 - and the 64 bit build, i had to pull 3 classes over from
170 // cmdlib.cpp even with the proper includes to get rid of the lnk2001 error
171 //
172 // amckern - amckern@yahoo.com
173 // =====================================================================================
174
175 #define PATHSEPARATOR(c) ((c) == '\\' || (c) == '/')
176
ExtractFileBase(const char * const path,char * dest)177 void ExtractFileBase(const char* const path, char* dest)
178 {
179 hlassert (path != dest);
180
181 const char* src;
182
183 src = path + strlen(path) - 1;
184
185 //
186 // back up until a \ or the start
187 //
188 while (src != path && !PATHSEPARATOR(*(src - 1)))
189 src--;
190
191 while (*src && *src != '.')
192 {
193 *dest++ = *src++;
194 }
195 *dest = 0;
196 }
197
ExtractFilePath(const char * const path,char * dest)198 void ExtractFilePath(const char* const path, char* dest)
199 {
200 hlassert (path != dest);
201
202 const char* src;
203
204 src = path + strlen(path) - 1;
205
206 //
207 // back up until a \ or the start
208 //
209 while (src != path && !PATHSEPARATOR(*(src - 1)))
210 src--;
211
212 memcpy(dest, path, src - path);
213 dest[src - path] = 0;
214 }
215
ExtractFile(const char * const path,char * dest)216 void ExtractFile(const char* const path, char* dest)
217 {
218 hlassert (path != dest);
219
220 const char* src;
221
222 src = path + strlen(path) - 1;
223
224 while (src != path && !PATHSEPARATOR(*(src - 1)))
225 src--;
226
227 while (*src)
228 {
229 *dest++ = *src++;
230 }
231 *dest = 0;
232 }
233
234 // =====================================================================================
235 // NewFaceFromFace
236 // Duplicates the non point information of a face, used by SplitFace and MergeFace.
237 // =====================================================================================
NewFaceFromFace(const face_t * const in)238 face_t* NewFaceFromFace(const face_t* const in)
239 {
240 face_t* newf;
241
242 newf = AllocFace();
243
244 newf->planenum = in->planenum;
245 newf->texturenum = in->texturenum;
246 newf->original = in->original;
247 newf->contents = in->contents;
248
249 return newf;
250 }
251
252 // =====================================================================================
253 // SplitFaceTmp
254 // blah
255 // =====================================================================================
SplitFaceTmp(face_t * in,const dplane_t * const split,face_t ** front,face_t ** back)256 static void SplitFaceTmp(face_t* in, const dplane_t* const split, face_t** front, face_t** back)
257 {
258 vec_t dists[MAXEDGES + 1];
259 int sides[MAXEDGES + 1];
260 int counts[3];
261 vec_t dot;
262 int i;
263 int j;
264 face_t* newf;
265 face_t* new2;
266 vec_t* p1;
267 vec_t* p2;
268 vec3_t mid;
269
270 if (in->numpoints < 0)
271 {
272 Error("SplitFace: freed face");
273 }
274 counts[0] = counts[1] = counts[2] = 0;
275
276 // determine sides for each point
277 for (i = 0; i < in->numpoints; i++)
278 {
279 dot = DotProduct(in->pts[i], split->normal);
280 dot -= split->dist;
281 dists[i] = dot;
282 if (dot > ON_EPSILON)
283 {
284 sides[i] = SIDE_FRONT;
285 }
286 else if (dot < -ON_EPSILON)
287 {
288 sides[i] = SIDE_BACK;
289 }
290 else
291 {
292 sides[i] = SIDE_ON;
293 }
294 counts[sides[i]]++;
295 }
296 sides[i] = sides[0];
297 dists[i] = dists[0];
298
299 if (!counts[0])
300 {
301 *front = NULL;
302 *back = in;
303 return;
304 }
305 if (!counts[1])
306 {
307 *front = in;
308 *back = NULL;
309 return;
310 }
311
312 *back = newf = NewFaceFromFace(in);
313 *front = new2 = NewFaceFromFace(in);
314
315 // distribute the points and generate splits
316
317 for (i = 0; i < in->numpoints; i++)
318 {
319 if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
320 {
321 Error("SplitFace: numpoints > MAXEDGES");
322 }
323
324 p1 = in->pts[i];
325
326 if (sides[i] == SIDE_ON)
327 {
328 VectorCopy(p1, newf->pts[newf->numpoints]);
329 newf->numpoints++;
330 VectorCopy(p1, new2->pts[new2->numpoints]);
331 new2->numpoints++;
332 continue;
333 }
334
335 if (sides[i] == SIDE_FRONT)
336 {
337 VectorCopy(p1, new2->pts[new2->numpoints]);
338 new2->numpoints++;
339 }
340 else
341 {
342 VectorCopy(p1, newf->pts[newf->numpoints]);
343 newf->numpoints++;
344 }
345
346 if (sides[i + 1] == SIDE_ON || sides[i + 1] == sides[i])
347 {
348 continue;
349 }
350
351 // generate a split point
352 p2 = in->pts[(i + 1) % in->numpoints];
353
354 dot = dists[i] / (dists[i] - dists[i + 1]);
355 for (j = 0; j < 3; j++)
356 { // avoid round off error when possible
357 if (split->normal[j] == 1)
358 {
359 mid[j] = split->dist;
360 }
361 else if (split->normal[j] == -1)
362 {
363 mid[j] = -split->dist;
364 }
365 else
366 {
367 mid[j] = p1[j] + dot * (p2[j] - p1[j]);
368 }
369 }
370
371 VectorCopy(mid, newf->pts[newf->numpoints]);
372 newf->numpoints++;
373 VectorCopy(mid, new2->pts[new2->numpoints]);
374 new2->numpoints++;
375 }
376
377 if (newf->numpoints > MAXEDGES || new2->numpoints > MAXEDGES)
378 {
379 Error("SplitFace: numpoints > MAXEDGES");
380 }
381 }
382
383 // =====================================================================================
384 // SplitFace
385 // blah
386 // =====================================================================================
SplitFace(face_t * in,const dplane_t * const split,face_t ** front,face_t ** back)387 void SplitFace(face_t* in, const dplane_t* const split, face_t** front, face_t** back)
388 {
389 SplitFaceTmp(in, split, front, back);
390
391 // free the original face now that is is represented by the fragments
392 if (*front && *back)
393 {
394 FreeFace(in);
395 }
396 }
397
398 // =====================================================================================
399 // AllocFace
400 // =====================================================================================
AllocFace()401 face_t* AllocFace()
402 {
403 face_t* f;
404
405 f = (face_t*)malloc(sizeof(face_t));
406 memset(f, 0, sizeof(face_t));
407
408 f->planenum = -1;
409
410 return f;
411 }
412
413 // =====================================================================================
414 // FreeFace
415 // =====================================================================================
FreeFace(face_t * f)416 void FreeFace(face_t* f)
417 {
418 free(f);
419 }
420
421 // =====================================================================================
422 // AllocSurface
423 // =====================================================================================
AllocSurface()424 surface_t* AllocSurface()
425 {
426 surface_t* s;
427
428 s = (surface_t*)malloc(sizeof(surface_t));
429 memset(s, 0, sizeof(surface_t));
430
431 return s;
432 }
433
434 // =====================================================================================
435 // FreeSurface
436 // =====================================================================================
FreeSurface(surface_t * s)437 void FreeSurface(surface_t* s)
438 {
439 free(s);
440 }
441
442 // =====================================================================================
443 // AllocPortal
444 // =====================================================================================
AllocPortal()445 portal_t* AllocPortal()
446 {
447 portal_t* p;
448
449 p = (portal_t*)malloc(sizeof(portal_t));
450 memset(p, 0, sizeof(portal_t));
451
452 return p;
453 }
454
455 // =====================================================================================
456 // FreePortal
457 // =====================================================================================
FreePortal(portal_t * p)458 void FreePortal(portal_t* p) // consider: inline
459 {
460 free(p);
461 }
462
463
464 // =====================================================================================
465 // AllocNode
466 // blah
467 // =====================================================================================
AllocNode()468 node_t* AllocNode()
469 {
470 node_t* n;
471
472 n = (node_t*)malloc(sizeof(node_t));
473 memset(n, 0, sizeof(node_t));
474
475 return n;
476 }
477
478 // =====================================================================================
479 // AddPointToBounds
480 // =====================================================================================
AddPointToBounds(const vec3_t v,vec3_t mins,vec3_t maxs)481 void AddPointToBounds(const vec3_t v, vec3_t mins, vec3_t maxs)
482 {
483 int i;
484 vec_t val;
485
486 for (i = 0; i < 3; i++)
487 {
488 val = v[i];
489 if (val < mins[i])
490 {
491 mins[i] = val;
492 }
493 if (val > maxs[i])
494 {
495 maxs[i] = val;
496 }
497 }
498 }
499
500 // =====================================================================================
501 // AddFaceToBounds
502 // =====================================================================================
AddFaceToBounds(const face_t * const f,vec3_t mins,vec3_t maxs)503 static void AddFaceToBounds(const face_t* const f, vec3_t mins, vec3_t maxs)
504 {
505 int i;
506
507 for (i = 0; i < f->numpoints; i++)
508 {
509 AddPointToBounds(f->pts[i], mins, maxs);
510 }
511 }
512
513 // =====================================================================================
514 // ClearBounds
515 // =====================================================================================
ClearBounds(vec3_t mins,vec3_t maxs)516 static void ClearBounds(vec3_t mins, vec3_t maxs)
517 {
518 mins[0] = mins[1] = mins[2] = 99999;
519 maxs[0] = maxs[1] = maxs[2] = -99999;
520 }
521
522 // =====================================================================================
523 // SurflistFromValidFaces
524 // blah
525 // =====================================================================================
SurflistFromValidFaces()526 static surfchain_t* SurflistFromValidFaces()
527 {
528 surface_t* n;
529 int i;
530 face_t* f;
531 face_t* next;
532 surfchain_t* sc;
533
534 sc = (surfchain_t*)malloc(sizeof(*sc));
535 ClearBounds(sc->mins, sc->maxs);
536 sc->surfaces = NULL;
537
538 // grab planes from both sides
539 for (i = 0; i < g_numplanes; i += 2)
540 {
541 if (!validfaces[i] && !validfaces[i + 1])
542 {
543 continue;
544 }
545 n = AllocSurface();
546 n->next = sc->surfaces;
547 sc->surfaces = n;
548 ClearBounds(n->mins, n->maxs);
549 n->planenum = i;
550
551 n->faces = NULL;
552 for (f = validfaces[i]; f; f = next)
553 {
554 next = f->next;
555 f->next = n->faces;
556 n->faces = f;
557 AddFaceToBounds(f, n->mins, n->maxs);
558 }
559 for (f = validfaces[i + 1]; f; f = next)
560 {
561 next = f->next;
562 f->next = n->faces;
563 n->faces = f;
564 AddFaceToBounds(f, n->mins, n->maxs);
565 }
566
567 AddPointToBounds(n->mins, sc->mins, sc->maxs);
568 AddPointToBounds(n->maxs, sc->mins, sc->maxs);
569
570 validfaces[i] = NULL;
571 validfaces[i + 1] = NULL;
572 }
573
574 // merge all possible polygons
575
576 MergeAll(sc->surfaces);
577
578 return sc;
579 }
580
581 #ifdef ZHLT_NULLTEX// AJM
582 // =====================================================================================
583 // CheckFaceForNull
584 // Returns true if the passed face is facetype null
585 // =====================================================================================
CheckFaceForNull(const face_t * const f)586 bool CheckFaceForNull(const face_t* const f)
587 {
588 // null faces are only of facetype face_null if we are using null texture stripping
589 if (g_bUseNullTex)
590 {
591 texinfo_t* info;
592 miptex_t* miptex;
593 int ofs;
594
595 info = &g_texinfo[f->texturenum];
596 ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex];
597 miptex = (miptex_t*)(&g_dtexdata[ofs]);
598
599 if (!strcasecmp(miptex->name, "null"))
600 return true;
601 else
602 return false;
603 }
604 else // otherwise, under normal cases, null textured faces should be facetype face_normal
605 {
606 return false;
607 }
608 }
609 // =====================================================================================
610 //Cpt_Andrew - UTSky Check
611 // =====================================================================================
CheckFaceForEnv_Sky(const face_t * const f)612 bool CheckFaceForEnv_Sky(const face_t* const f)
613 {
614 texinfo_t* info;
615 miptex_t* miptex;
616 int ofs;
617
618 info = &g_texinfo[f->texturenum];
619 ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex];
620 miptex = (miptex_t*)(&g_dtexdata[ofs]);
621
622 if (!strcasecmp(miptex->name, "env_sky"))
623 return true;
624 else
625 return false;
626 }
627 // =====================================================================================
628
629
630
631
632 #endif
633
634 #ifdef ZHLT_DETAIL
635 // =====================================================================================
636 // CheckFaceForDetail
637 // Returns true if the passed face is part of a detail brush
638 // =====================================================================================
CheckFaceForDetail(const face_t * const f)639 bool CheckFaceForDetail(const face_t* const f)
640 {
641 if (f->contents == CONTENTS_DETAIL)
642 {
643 //Log("CheckFaceForDetail:: got a detail face");
644 return true;
645 }
646
647 return false;
648 }
649 #endif
650
651 // =====================================================================================
652 // CheckFaceForHint
653 // Returns true if the passed face is facetype hint
654 // =====================================================================================
CheckFaceForHint(const face_t * const f)655 bool CheckFaceForHint(const face_t* const f)
656 {
657 texinfo_t* info;
658 miptex_t* miptex;
659 int ofs;
660
661 info = &g_texinfo[f->texturenum];
662 ofs = ((dmiptexlump_t *)g_dtexdata)->dataofs[info->miptex];
663 miptex = (miptex_t *)(&g_dtexdata[ofs]);
664
665 if (!strcasecmp(miptex->name, "hint"))
666 {
667 return true;
668 }
669 else
670 {
671 return false;
672 }
673 }
674
675 // =====================================================================================
676 // CheckFaceForSkipt
677 // Returns true if the passed face is facetype skip
678 // =====================================================================================
CheckFaceForSkip(const face_t * const f)679 bool CheckFaceForSkip(const face_t* const f)
680 {
681 texinfo_t* info;
682 miptex_t* miptex;
683 int ofs;
684
685 info = &g_texinfo[f->texturenum];
686 ofs = ((dmiptexlump_t*)g_dtexdata)->dataofs[info->miptex];
687 miptex = (miptex_t*)(&g_dtexdata[ofs]);
688
689 if (!strcasecmp(miptex->name, "skip"))
690 {
691 return true;
692 }
693 else
694 {
695 return false;
696 }
697 }
698
699 // =====================================================================================
700 // SetFaceType
701 // =====================================================================================
SetFaceType(face_t * f)702 static facestyle_e SetFaceType(face_t* f)
703 {
704 if (CheckFaceForHint(f))
705 {
706 f->facestyle = face_hint;
707 }
708 else if (CheckFaceForSkip(f))
709 {
710 f->facestyle = face_skip;
711 }
712 #ifdef ZHLT_NULLTEX // AJM
713 else if (CheckFaceForNull(f))
714 {
715 f->facestyle = face_null;
716 }
717 #endif
718
719 // =====================================================================================
720 //Cpt_Andrew - Env_Sky Check
721 // =====================================================================================
722 //else if (CheckFaceForUTSky(f))
723 else if (CheckFaceForEnv_Sky(f))
724 {
725 f->facestyle = face_null;
726 }
727 // =====================================================================================
728
729
730 #ifdef ZHLT_DETAIL
731 else if (CheckFaceForDetail(f))
732 {
733 //Log("SetFaceType::detail face\n");
734 f->facestyle = face_detail;
735 }
736 #endif
737 else
738 {
739 f->facestyle = face_normal;
740 }
741 return f->facestyle;
742 }
743
744 // =====================================================================================
745 // ReadSurfs
746 // =====================================================================================
ReadSurfs(FILE * file)747 static surfchain_t* ReadSurfs(FILE* file)
748 {
749 int r;
750 int planenum, g_texinfo, contents, numpoints;
751 face_t* f;
752 int i;
753 double v[3];
754 int line = 0;
755
756 // read in the polygons
757 while (1)
758 {
759 line++;
760 r = fscanf(file, "%i %i %i %i\n", &planenum, &g_texinfo, &contents, &numpoints);
761 if (r == 0 || r == -1)
762 {
763 return NULL;
764 }
765 if (planenum == -1) // end of model
766 {
767 break;
768 }
769 if (r != 4)
770 {
771 Error("ReadSurfs (line %i): scanf failure", line);
772 }
773 if (numpoints > MAXPOINTS)
774 {
775 Error("ReadSurfs (line %i): %i > MAXPOINTS\nThis is caused by a face with too many verticies (typically found on end-caps of high-poly cylinders)\n", line, numpoints);
776 }
777 if (planenum > g_numplanes)
778 {
779 Error("ReadSurfs (line %i): %i > g_numplanes\n", line, planenum);
780 }
781 if (g_texinfo > g_numtexinfo)
782 {
783 Error("ReadSurfs (line %i): %i > g_numtexinfo", line, g_texinfo);
784 }
785
786 if (!strcasecmp(GetTextureByNumber(g_texinfo), "skip"))
787 {
788 Verbose("ReadSurfs (line %i): skipping a surface", line);
789
790 for (i = 0; i < numpoints; i++)
791 {
792 line++;
793 //Verbose("skipping line %d", line);
794 r = fscanf(file, "%lf %lf %lf\n", &v[0], &v[1], &v[2]);
795 if (r != 3)
796 {
797 Error("::ReadSurfs (face_skip), fscanf of points failed at line %i", line);
798 }
799 }
800 fscanf(file, "\n");
801 continue;
802 }
803
804 f = AllocFace();
805 f->planenum = planenum;
806 f->texturenum = g_texinfo;
807 f->contents = contents;
808 f->numpoints = numpoints;
809 f->next = validfaces[planenum];
810 validfaces[planenum] = f;
811
812 SetFaceType(f);
813
814 for (i = 0; i < f->numpoints; i++)
815 {
816 line++;
817 r = fscanf(file, "%lf %lf %lf\n", &v[0], &v[1], &v[2]);
818 if (r != 3)
819 {
820 Error("::ReadSurfs (face_normal), fscanf of points failed at line %i", line);
821 }
822 VectorCopy(v, f->pts[i]);
823 }
824 fscanf(file, "\n");
825 }
826
827 return SurflistFromValidFaces();
828 }
829
830
831 #ifdef HLBSP_THREADS// AJM
832 // =====================================================================================
833 // ProcessModelThreaded
834 // time to compl
835 // =====================================================================================
ProcessModel(intptr_t modelnum)836 void ProcessModel(intptr_t modelnum)
837 {
838 surfchain_t* surfs;
839 node_t* nodes;
840 dmodel_t* model;
841 int startleafs;
842
843 surfs = ReadSurfs(polyfiles[0]);
844
845 if (!surfs)
846 return; // all models are done
847
848 hlassume(g_nummodels < MAX_MAP_MODELS, assume_MAX_MAP_MODELS);
849
850 startleafs = g_numleafs;
851 int modnum = g_nummodels;
852 model = &g_dmodels[modnum];
853 g_nummodels++;
854
855 // Log("ProcessModel: %i (%i f)\n", modnum, model->numfaces);
856
857 VectorCopy(surfs->mins, model->mins);
858 VectorCopy(surfs->maxs, model->maxs);
859
860 // SolidBSP generates a node tree
861 nodes = SolidBSP(surfs);
862
863 // build all the portals in the bsp tree
864 // some portals are solid polygons, and some are paths to other leafs
865 if (g_nummodels == 1 && !g_nofill) // assume non-world bmodels are simple
866 {
867 nodes = FillOutside(nodes, (g_bLeaked != true), 0); // make a leakfile if bad
868 }
869
870 FreePortals(nodes);
871
872 // fix tjunctions
873 tjunc(nodes);
874
875 MakeFaceEdges();
876
877 // emit the faces for the bsp file
878 model->headnode[0] = g_numnodes;
879 model->firstface = g_numfaces;
880 WriteDrawNodes(nodes);
881 model->numfaces = g_numfaces - model->firstface;;
882 model->visleafs = g_numleafs - startleafs;
883
884 if (g_noclip)
885 {
886 return true;
887 }
888
889 // the clipping hulls are simpler
890 for (g_hullnum = 1; g_hullnum < NUM_HULLS; g_hullnum++)
891 {
892 surfs = ReadSurfs(polyfiles[g_hullnum]);
893 nodes = SolidBSP(surfs);
894 if (g_nummodels == 1 && !g_nofill) // assume non-world bmodels are simple
895 {
896 nodes = FillOutside(nodes, (g_bLeaked != true), g_hullnum);
897 }
898 FreePortals(nodes);
899 model->headnode[g_hullnum] = g_numclipnodes;
900 WriteClipNodes(nodes);
901 }
902
903 return true;
904 }
905
906 #else
907 // =====================================================================================
908 // ProcessModel
909 // =====================================================================================
ProcessModel()910 static bool ProcessModel()
911 {
912 surfchain_t* surfs;
913 node_t* nodes;
914 dmodel_t* model;
915 int startleafs;
916
917 surfs = ReadSurfs(polyfiles[0]);
918
919 if (!surfs)
920 return false; // all models are done
921
922 hlassume(g_nummodels < MAX_MAP_MODELS, assume_MAX_MAP_MODELS);
923
924 startleafs = g_numleafs;
925 int modnum = g_nummodels;
926 model = &g_dmodels[modnum];
927 g_nummodels++;
928
929 // Log("ProcessModel: %i (%i f)\n", modnum, model->numfaces);
930
931 VectorCopy(surfs->mins, model->mins);
932 VectorCopy(surfs->maxs, model->maxs);
933
934 // SolidBSP generates a node tree
935 nodes = SolidBSP(surfs,modnum==0);
936
937 // build all the portals in the bsp tree
938 // some portals are solid polygons, and some are paths to other leafs
939 if (g_nummodels == 1 && !g_nofill) // assume non-world bmodels are simple
940 {
941 nodes = FillOutside(nodes, (g_bLeaked != true), 0); // make a leakfile if bad
942 }
943
944 FreePortals(nodes);
945
946 // fix tjunctions
947 tjunc(nodes);
948
949 MakeFaceEdges();
950
951 // emit the faces for the bsp file
952 model->headnode[0] = g_numnodes;
953 model->firstface = g_numfaces;
954 WriteDrawNodes(nodes);
955 model->numfaces = g_numfaces - model->firstface;;
956 model->visleafs = g_numleafs - startleafs;
957
958 if (g_noclip)
959 {
960 /*
961 KGP 12/31/03 - store empty content type in headnode pointers to signify
962 lack of clipping information in a way that doesn't crash the half-life
963 engine at runtime.
964 */
965 model->headnode[1] = CONTENTS_EMPTY;
966 model->headnode[2] = CONTENTS_EMPTY;
967 model->headnode[3] = CONTENTS_EMPTY;
968 return true;
969 }
970
971 // the clipping hulls are simpler
972 for (g_hullnum = 1; g_hullnum < NUM_HULLS; g_hullnum++)
973 {
974 surfs = ReadSurfs(polyfiles[g_hullnum]);
975 nodes = SolidBSP(surfs,modnum==0);
976 if (g_nummodels == 1 && !g_nofill) // assume non-world bmodels are simple
977 {
978 nodes = FillOutside(nodes, (g_bLeaked != true), g_hullnum);
979 }
980 FreePortals(nodes);
981 /*
982 KGP 12/31/03 - need to test that the head clip node isn't empty; if it is
983 we need to set model->headnode equal to the content type of the head, or create
984 a trivial single-node case where the content type is the same for both leaves
985 if setting the content type is invalid.
986 */
987 if(nodes->planenum == -1) //empty!
988 {
989 model->headnode[g_hullnum] = nodes->contents;
990 }
991 else
992 {
993 model->headnode[g_hullnum] = g_numclipnodes;
994 WriteClipNodes(nodes);
995 }
996 }
997
998 return true;
999 }
1000 #endif
1001
1002 // =====================================================================================
1003 // Usage
1004 // =====================================================================================
Usage()1005 static void Usage()
1006 {
1007 Banner();
1008
1009 Log("\n-= %s Options =-\n\n", g_Program);
1010 Log(" -leakonly : Run BSP only enough to check for LEAKs\n");
1011 Log(" -subdivide # : Sets the face subdivide size\n");
1012 Log(" -maxnodesize # : Sets the maximum portal node size\n\n");
1013 Log(" -notjunc : Don't break edges on t-junctions (not for final runs)\n");
1014 Log(" -noclip : Don't process the clipping hull (not for final runs)\n");
1015 Log(" -nofill : Don't fill outside (will mask LEAKs) (not for final runs)\n");
1016 Log(" -noopt : Don't optimize planes on BSP write (not for final runs)\n\n");
1017 Log(" -texdata # : Alter maximum texture memory limit (in kb)\n");
1018 Log(" -lightdata # : Alter maximum lighting memory limit (in kb)\n");
1019 Log(" -chart : display bsp statitics\n");
1020 Log(" -low | -high : run program an altered priority level\n");
1021 Log(" -nolog : don't generate the compile logfiles\n");
1022 Log(" -threads # : manually specify the number of threads to run\n");
1023 #ifdef SYSTEM_WIN32
1024 Log(" -estimate : display estimated time during compile\n");
1025 #endif
1026 #ifdef ZHLT_PROGRESSFILE // AJM
1027 Log(" -progressfile path : specify the path to a file for progress estimate output\n");
1028 #endif
1029 #ifdef SYSTEM_POSIX
1030 Log(" -noestimate : do not display continuous compile time estimates\n");
1031 #endif
1032
1033 #ifdef ZHLT_NULLTEX // AJM
1034 Log(" -nonulltex : Don't strip NULL faces\n");
1035 #endif
1036
1037 #ifdef ZHLT_DETAIL // AJM
1038 Log(" -nodetail : don't handle detail brushes\n");
1039 #endif
1040
1041 Log(" -verbose : compile with verbose messages\n");
1042 Log(" -noinfo : Do not show tool configuration information\n");
1043 Log(" -dev # : compile with developer message\n\n");
1044 Log(" mapfile : The mapfile to compile\n\n");
1045
1046 exit(1);
1047 }
1048
1049 // =====================================================================================
1050 // Settings
1051 // =====================================================================================
Settings()1052 static void Settings()
1053 {
1054 char* tmp;
1055
1056 if (!g_info)
1057 return;
1058
1059 Log("\nCurrent %s Settings\n", g_Program);
1060 Log("Name | Setting | Default\n" "-------------------|-----------|-------------------------\n");
1061
1062 // ZHLT Common Settings
1063 if (DEFAULT_NUMTHREADS == -1)
1064 {
1065 Log("threads [ %7d ] [ Varies ]\n", g_numthreads);
1066 }
1067 else
1068 {
1069 Log("threads [ %7d ] [ %7d ]\n", g_numthreads, DEFAULT_NUMTHREADS);
1070 }
1071
1072 Log("verbose [ %7s ] [ %7s ]\n", g_verbose ? "on" : "off", DEFAULT_VERBOSE ? "on" : "off");
1073 Log("log [ %7s ] [ %7s ]\n", g_log ? "on" : "off", DEFAULT_LOG ? "on" : "off");
1074 Log("developer [ %7d ] [ %7d ]\n", g_developer, DEFAULT_DEVELOPER);
1075 Log("chart [ %7s ] [ %7s ]\n", g_chart ? "on" : "off", DEFAULT_CHART ? "on" : "off");
1076 Log("estimate [ %7s ] [ %7s ]\n", g_estimate ? "on" : "off", DEFAULT_ESTIMATE ? "on" : "off");
1077 Log("max texture memory [ %7d ] [ %7d ]\n", g_max_map_miptex, DEFAULT_MAX_MAP_MIPTEX);
1078
1079 switch (g_threadpriority)
1080 {
1081 case eThreadPriorityNormal:
1082 default:
1083 tmp = "Normal";
1084 break;
1085 case eThreadPriorityLow:
1086 tmp = "Low";
1087 break;
1088 case eThreadPriorityHigh:
1089 tmp = "High";
1090 break;
1091 }
1092 Log("priority [ %7s ] [ %7s ]\n", tmp, "Normal");
1093 Log("\n");
1094
1095 // HLBSP Specific Settings
1096 Log("noclip [ %7s ] [ %7s ]\n", g_noclip ? "on" : "off", DEFAULT_NOCLIP ? "on" : "off");
1097 Log("nofill [ %7s ] [ %7s ]\n", g_nofill ? "on" : "off", DEFAULT_NOFILL ? "on" : "off");
1098 Log("noopt [ %7s ] [ %7s ]\n", g_noopt ? "on" : "off", DEFAULT_NOOPT ? "on" : "off");
1099 #ifdef ZHLT_NULLTEX // AJM
1100 Log("null tex. stripping [ %7s ] [ %7s ]\n", g_bUseNullTex ? "on" : "off", DEFAULT_NULLTEX ? "on" : "off" );
1101 #endif
1102 #ifdef ZHLT_DETAIL // AJM
1103 Log("detail brushes [ %7s ] [ %7s ]\n", g_bDetailBrushes ? "on" : "off", DEFAULT_DETAIL ? "on" : "off" );
1104 #endif
1105 Log("notjunc [ %7s ] [ %7s ]\n", g_notjunc ? "on" : "off", DEFAULT_NOTJUNC ? "on" : "off");
1106 Log("subdivide size [ %7d ] [ %7d ] (Min %d) (Max %d)\n",
1107 g_subdivide_size, DEFAULT_SUBDIVIDE_SIZE, MIN_SUBDIVIDE_SIZE, MAX_SUBDIVIDE_SIZE);
1108 Log("max node size [ %7d ] [ %7d ] (Min %d) (Max %d)\n",
1109 g_maxnode_size, DEFAULT_MAXNODE_SIZE, MIN_MAXNODE_SIZE, MAX_MAXNODE_SIZE);
1110
1111 Log("\n\n");
1112 }
1113
1114 // =====================================================================================
1115 // ProcessFile
1116 // =====================================================================================
ProcessFile(const char * const filename)1117 static void ProcessFile(const char* const filename)
1118 {
1119 int i;
1120 char name[_MAX_PATH];
1121
1122 // delete existing files
1123 safe_snprintf(g_portfilename, _MAX_PATH, "%s.prt", filename);
1124 unlink(g_portfilename);
1125
1126 safe_snprintf(g_pointfilename, _MAX_PATH, "%s.pts", filename);
1127 unlink(g_pointfilename);
1128
1129 safe_snprintf(g_linefilename, _MAX_PATH, "%s.lin", filename);
1130 unlink(g_linefilename);
1131
1132 // open the hull files
1133 for (i = 0; i < NUM_HULLS; i++)
1134 {
1135 //mapname.p[0-3]
1136 sprintf(name, "%s.p%i", filename, i);
1137 polyfiles[i] = fopen(name, "r");
1138
1139 if (!polyfiles[i])
1140 Error("Can't open %s", name);
1141 }
1142
1143 // load the output of csg
1144 safe_snprintf(g_bspfilename, _MAX_PATH, "%s.bsp", filename);
1145 LoadBSPFile(g_bspfilename);
1146 ParseEntities();
1147
1148 Settings(); // AJM: moved here due to info_compile_parameters entity
1149
1150 // init the tables to be shared by all models
1151 BeginBSPFile();
1152
1153 #ifdef HLBSP_THREADS // AJM
1154 NamedRunThreadsOnIndividual(nummodels, g_estimate, ProcessModel);
1155 #else
1156 // process each model individually
1157 while (ProcessModel())
1158 ;
1159 #endif
1160
1161 // write the updated bsp file out
1162 FinishBSPFile();
1163 }
1164
1165 // =====================================================================================
1166 // main
1167 // =====================================================================================
main(const int argc,char ** argv)1168 int main(const int argc, char** argv)
1169 {
1170 int i;
1171 double start, end;
1172 const char* mapname_from_arg = NULL;
1173
1174 g_Program = "hlbsp";
1175
1176 // if we dont have any command line argvars, print out usage and die
1177 if (argc == 1)
1178 Usage();
1179
1180 // check command line args
1181 for (i = 1; i < argc; i++)
1182 {
1183 if (!strcasecmp(argv[i], "-threads"))
1184 {
1185 if (i < argc)
1186 {
1187 int g_numthreads = atoi(argv[++i]);
1188
1189 if (g_numthreads < 1)
1190 {
1191 Log("Expected value of at least 1 for '-threads'\n");
1192 Usage();
1193 }
1194 }
1195 else
1196 {
1197 Usage();
1198 }
1199 }
1200 else if (!strcasecmp(argv[i], "-notjunc"))
1201 {
1202 g_notjunc = true;
1203 }
1204 else if (!strcasecmp(argv[i], "-noclip"))
1205 {
1206 g_noclip = true;
1207 }
1208 else if (!strcasecmp(argv[i], "-nofill"))
1209 {
1210 g_nofill = true;
1211 }
1212
1213 #ifdef SYSTEM_WIN32
1214 else if (!strcasecmp(argv[i], "-estimate"))
1215 {
1216 g_estimate = true;
1217 }
1218 #endif
1219
1220 #ifdef SYSTEM_POSIX
1221 else if (!strcasecmp(argv[i], "-noestimate"))
1222 {
1223 g_estimate = false;
1224 }
1225 #endif
1226
1227 #ifdef ZHLT_NETVIS
1228 else if (!strcasecmp(argv[i], "-client"))
1229 {
1230 if (i < argc)
1231 {
1232 g_clientid = atoi(argv[++i]);
1233 }
1234 else
1235 {
1236 Usage();
1237 }
1238 }
1239 #endif
1240
1241 #ifdef ZHLT_PROGRESSFILE // AJM
1242 else if (!strcasecmp(argv[i], "-progressfile"))
1243 {
1244 if (i < argc)
1245 {
1246 g_progressfile = argv[++i];
1247 }
1248 else
1249 {
1250 Log("Error: -progressfile: expected path to progress file following parameter\n");
1251 Usage();
1252 }
1253 }
1254 #endif
1255
1256 else if (!strcasecmp(argv[i], "-dev"))
1257 {
1258 if (i < argc)
1259 {
1260 g_developer = (developer_level_t)atoi(argv[++i]);
1261 }
1262 else
1263 {
1264 Usage();
1265 }
1266 }
1267 else if (!strcasecmp(argv[i], "-verbose"))
1268 {
1269 g_verbose = true;
1270 }
1271 else if (!strcasecmp(argv[i], "-noinfo"))
1272 {
1273 g_info = false;
1274 }
1275 else if (!strcasecmp(argv[i], "-leakonly"))
1276 {
1277 g_bLeakOnly = true;
1278 }
1279 else if (!strcasecmp(argv[i], "-chart"))
1280 {
1281 g_chart = true;
1282 }
1283 else if (!strcasecmp(argv[i], "-low"))
1284 {
1285 g_threadpriority = eThreadPriorityLow;
1286 }
1287 else if (!strcasecmp(argv[i], "-high"))
1288 {
1289 g_threadpriority = eThreadPriorityHigh;
1290 }
1291 else if (!strcasecmp(argv[i], "-nolog"))
1292 {
1293 g_log = false;
1294 }
1295
1296 #ifdef ZHLT_NULLTEX // AJM
1297 else if (!strcasecmp(argv[i], "-nonulltex"))
1298 {
1299 g_bUseNullTex = false;
1300 }
1301 #endif
1302
1303 #ifdef ZHLT_DETAIL // AJM
1304 else if (!strcasecmp(argv[i], "-nodetail"))
1305 {
1306 g_bDetailBrushes = false;
1307 }
1308 #endif
1309 else if (!strcasecmp(argv[i], "-noopt"))
1310 {
1311 g_noopt = true;
1312 }
1313 else if (!strcasecmp(argv[i], "-subdivide"))
1314 {
1315 if (i < argc)
1316 {
1317 g_subdivide_size = atoi(argv[++i]);
1318 if (g_subdivide_size > MAX_SUBDIVIDE_SIZE)
1319 {
1320 Warning
1321 ("Maximum value for subdivide size is %i, '-subdivide %i' ignored",
1322 MAX_SUBDIVIDE_SIZE, g_subdivide_size);
1323 g_subdivide_size = MAX_SUBDIVIDE_SIZE;
1324 }
1325 else if (g_subdivide_size < MIN_SUBDIVIDE_SIZE)
1326 {
1327 Warning
1328 ("Mininum value for subdivide size is %i, '-subdivide %i' ignored",
1329 MIN_SUBDIVIDE_SIZE, g_subdivide_size);
1330 g_subdivide_size = MAX_SUBDIVIDE_SIZE;
1331 }
1332 }
1333 else
1334 {
1335 Usage();
1336 }
1337 }
1338 else if (!strcasecmp(argv[i], "-maxnodesize"))
1339 {
1340 if (i < argc)
1341 {
1342 g_maxnode_size = atoi(argv[++i]);
1343 if (g_maxnode_size > MAX_MAXNODE_SIZE)
1344 {
1345 Warning
1346 ("Maximum value for max node size is %i, '-maxnodesize %i' ignored",
1347 MAX_MAXNODE_SIZE, g_maxnode_size);
1348 g_maxnode_size = MAX_MAXNODE_SIZE;
1349 }
1350 else if (g_maxnode_size < MIN_MAXNODE_SIZE)
1351 {
1352 Warning
1353 ("Mininimum value for max node size is %i, '-maxnodesize %i' ignored",
1354 MIN_MAXNODE_SIZE, g_maxnode_size);
1355 g_maxnode_size = MAX_MAXNODE_SIZE;
1356 }
1357 }
1358 else
1359 {
1360 Usage();
1361 }
1362 }
1363 else if (!strcasecmp(argv[i], "-texdata"))
1364 {
1365 if (i < argc)
1366 {
1367 int x = atoi(argv[++i]) * 1024;
1368
1369 if (x > g_max_map_miptex)
1370 {
1371 g_max_map_miptex = x;
1372 }
1373 }
1374 else
1375 {
1376 Usage();
1377 }
1378 }
1379 else if (!strcasecmp(argv[i], "-lightdata"))
1380 {
1381 if (i < argc)
1382 {
1383 int x = atoi(argv[++i]) * 1024;
1384
1385 if (x > g_max_map_lightdata)
1386 {
1387 g_max_map_lightdata = x;
1388 }
1389 }
1390 else
1391 {
1392 Usage();
1393 }
1394 }
1395 else if (argv[i][0] == '-')
1396 {
1397 Log("Unknown option \"%s\"\n", argv[i]);
1398 Usage();
1399 }
1400 else if (!mapname_from_arg)
1401 {
1402 mapname_from_arg = argv[i];
1403 }
1404 else
1405 {
1406 Log("Unknown option \"%s\"\n", argv[i]);
1407 Usage();
1408 }
1409 }
1410
1411 if (!mapname_from_arg)
1412 {
1413 Log("No mapfile specified\n");
1414 Usage();
1415 }
1416
1417 safe_strncpy(g_Mapname, mapname_from_arg, _MAX_PATH);
1418 FlipSlashes(g_Mapname);
1419 StripExtension(g_Mapname);
1420 OpenLog(g_clientid);
1421 atexit(CloseLog);
1422 ThreadSetDefault();
1423 ThreadSetPriority(g_threadpriority);
1424 LogStart(argc, argv);
1425
1426 CheckForErrorLog();
1427
1428 dtexdata_init();
1429 atexit(dtexdata_free);
1430 //Settings();
1431 // END INIT
1432
1433 // Load the .void files for allowable entities in the void
1434 {
1435 char g_source[_MAX_PATH];
1436 char strSystemEntitiesVoidFile[_MAX_PATH];
1437 char strMapEntitiesVoidFile[_MAX_PATH];
1438
1439 safe_strncpy(g_source, mapname_from_arg, _MAX_PATH);
1440 StripExtension(g_source);
1441
1442 // try looking in the current directory
1443 safe_strncpy(strSystemEntitiesVoidFile, ENTITIES_VOID, _MAX_PATH);
1444 if (!q_exists(strSystemEntitiesVoidFile))
1445 {
1446 char tmp[_MAX_PATH];
1447 // try looking in the directory we were run from
1448 #ifdef SYSTEM_WIN32
1449 GetModuleFileName(NULL, tmp, _MAX_PATH);
1450 #else
1451 safe_strncpy(tmp, argv[0], _MAX_PATH);
1452 #endif
1453 ExtractFilePath(tmp, strSystemEntitiesVoidFile);
1454 safe_strncat(strSystemEntitiesVoidFile, ENTITIES_VOID, _MAX_PATH);
1455 }
1456
1457 // Set the optional level specific lights filename
1458 safe_strncpy(strMapEntitiesVoidFile, g_source, _MAX_PATH);
1459 DefaultExtension(strMapEntitiesVoidFile, ENTITIES_VOID_EXT);
1460
1461 LoadAllowableOutsideList(strSystemEntitiesVoidFile); // default entities.void
1462 if (*strMapEntitiesVoidFile)
1463 {
1464 LoadAllowableOutsideList(strMapEntitiesVoidFile); // automatic mapname.void
1465 }
1466 }
1467
1468 // BEGIN BSP
1469 start = I_FloatTime();
1470
1471 ProcessFile(g_Mapname);
1472
1473 end = I_FloatTime();
1474 LogTimeElapsed(end - start);
1475 // END BSP
1476
1477 FreeAllowableOutsideList();
1478
1479 return 0;
1480 }
1481