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