1 #pragma warning(disable: 4018) // '<' : signed/unsigned mismatch
2 
3 /*
4 
5     CONSTRUCTIVE SOLID GEOMETRY    -aka-    C S G
6 
7     Code based on original code from Valve Software,
8     Modified by Sean "Zoner" Cavanaugh (seanc@gearboxsoftware.com) with permission.
9     Modified by Tony "Merl" Moore (merlinis@bigpond.net.au) [AJM]
10 
11 */
12 
13 #include "csg.h"
14 
15 /*
16 
17  NOTES
18 
19  - check map size for +/- 4k limit at load time
20  - allow for multiple wad.cfg configurations per compile
21 
22 */
23 
24 static FILE*    out[NUM_HULLS]; // pointer to each of the hull out files (.p0, .p1, ect.)
25 static int      c_tiny;
26 static int      c_tiny_clip;
27 static int      c_outfaces;
28 static int      c_csgfaces;
29 BoundingBox     world_bounds;
30 
31 #ifdef HLCSG_WADCFG
32 char            wadconfigname[MAX_WAD_CFG_NAME];
33 #endif
34 
35 vec_t           g_tiny_threshold = DEFAULT_TINY_THRESHOLD;
36 
37 bool            g_noclip = DEFAULT_NOCLIP;              // no clipping hull "-noclip"
38 bool            g_onlyents = DEFAULT_ONLYENTS;          // onlyents mode "-onlyents"
39 bool            g_wadtextures = DEFAULT_WADTEXTURES;    // "-nowadtextures"
40 bool            g_chart = DEFAULT_CHART;                // show chart "-chart"
41 bool            g_skyclip = DEFAULT_SKYCLIP;            // no sky clipping "-noskyclip"
42 bool            g_estimate = DEFAULT_ESTIMATE;          // progress estimates "-estimate"
43 bool            g_info = DEFAULT_INFO;                  // "-info" ?
44 const char*     g_hullfile = NULL;                      // external hullfile "-hullfie sdfsd"
45 
46 #ifdef ZHLT_NULLTEX // AJM
47 bool            g_bUseNullTex = DEFAULT_NULLTEX;        // "-nonulltex"
48 #endif
49 
50 #ifdef HLCSG_PRECISIONCLIP // KGP
51 cliptype		g_cliptype = DEFAULT_CLIPTYPE;			// "-cliptype <value>"
52 #endif
53 
54 #ifdef HLCSG_NULLIFY_INVISIBLE
55 const char*			g_nullfile = NULL;
56 #endif
57 
58 #ifdef HLCSG_CLIPECONOMY // AJM
59 bool            g_bClipNazi = DEFAULT_CLIPNAZI;         // "-noclipeconomy"
60 #endif
61 
62 #ifdef HLCSG_AUTOWAD // AJM
63 bool            g_bWadAutoDetect = DEFAULT_WADAUTODETECT; // "-wadautodetect"
64 #endif
65 
66 #ifdef ZHLT_DETAIL // AJM
67 bool            g_bDetailBrushes = DEFAULT_DETAIL; // "-detail"
68 #endif
69 
70 #ifdef ZHLT_PROGRESSFILE // AJM
71 char*           g_progressfile = DEFAULT_PROGRESSFILE; // "-progressfile path"
72 #endif
73 
74 #ifdef ZHLT_INFO_COMPILE_PARAMETERS
75 // =====================================================================================
76 //  GetParamsFromEnt
77 //      parses entity keyvalues for setting information
78 // =====================================================================================
GetParamsFromEnt(entity_t * mapent)79 void            GetParamsFromEnt(entity_t* mapent)
80 {
81     int     iTmp;
82     char    szTmp[256];
83 
84     Log("\nCompile Settings detected from info_compile_parameters entity\n");
85 
86     // verbose(choices) : "Verbose compile messages" : 0 = [ 0 : "Off" 1 : "On" ]
87     iTmp = IntForKey(mapent, "verbose");
88     if (iTmp == 1)
89     {
90         g_verbose = true;
91     }
92     else if (iTmp == 0)
93     {
94         g_verbose = false;
95     }
96     Log("%30s [ %-9s ]\n", "Compile Option", "setting");
97     Log("%30s [ %-9s ]\n", "Verbose Compile Messages", g_verbose ? "on" : "off");
98 
99     // estimate(choices) :"Estimate Compile Times?" : 0 = [ 0: "Yes" 1: "No" ]
100     if (IntForKey(mapent, "estimate"))
101     {
102         g_estimate = true;
103     }
104     else
105     {
106         g_estimate = false;
107     }
108     Log("%30s [ %-9s ]\n", "Estimate Compile Times", g_estimate ? "on" : "off");
109 
110 	// priority(choices) : "Priority Level" : 0 = [	0 : "Normal" 1 : "High"	-1 : "Low" ]
111 	if (!strcmp(ValueForKey(mapent, "priority"), "1"))
112     {
113         g_threadpriority = eThreadPriorityHigh;
114         Log("%30s [ %-9s ]\n", "Thread Priority", "high");
115     }
116     else if (!strcmp(ValueForKey(mapent, "priority"), "-1"))
117     {
118         g_threadpriority = eThreadPriorityLow;
119         Log("%30s [ %-9s ]\n", "Thread Priority", "low");
120     }
121 
122     // texdata(string) : "Texture Data Memory" : "4096"
123     iTmp = IntForKey(mapent, "texdata") * 1024;
124     if (iTmp > g_max_map_miptex)
125     {
126         g_max_map_miptex = iTmp;
127     }
128     sprintf_s(szTmp, "%i", g_max_map_miptex);
129     Log("%30s [ %-9s ]\n", "Texture Data Memory", szTmp);
130 
131     // hullfile(string) : "Custom Hullfile"
132     if (ValueForKey(mapent, "hullfile"))
133     {
134         g_hullfile = ValueForKey(mapent, "hullfile");
135         Log("%30s [ %-9s ]\n", "Custom Hullfile", g_hullfile);
136     }
137 
138 #ifdef HLCSG_AUTOWAD
139     // wadautodetect(choices) : "Wad Auto Detect" : 0 =	[ 0 : "Off" 1 : "On" ]
140     if (!strcmp(ValueForKey(mapent, "wadautodetect"), "1"))
141     {
142         g_bWadAutoDetect = true;
143     }
144     else
145     {
146         g_bWadAutoDetect = false;
147     }
148     Log("%30s [ %-9s ]\n", "Wad Auto Detect", g_bWadAutoDetect ? "on" : "off");
149 #endif
150 
151 #ifdef HLCSG_WADCFG
152 	// wadconfig(string) : "Custom Wad Configuration" : ""
153     if (strlen(ValueForKey(mapent, "wadconfig")) > 0)
154     {
155         safe_strncpy(wadconfigname, ValueForKey(mapent, "wadconfig"), MAX_WAD_CFG_NAME);
156         Log("%30s [ %-9s ]\n", "Custom Wad Configuration", wadconfigname);
157     }
158 #endif
159 
160 #ifdef HLCSG_CLIPECONOMY
161     // noclipeconomy(choices) : "Strip Uneeded Clipnodes?" : 1 = [ 1 : "Yes" 0 : "No" ]
162     iTmp = IntForKey(mapent, "noclipeconomy");
163     if (iTmp == 1)
164     {
165         g_bClipNazi = true;
166     }
167     else if (iTmp == 0)
168     {
169         g_bClipNazi = false;
170     }
171     Log("%30s [ %-9s ]\n", "Clipnode Economy Mode", g_bClipNazi ? "on" : "off");
172 #endif
173 
174     /*
175     hlcsg(choices) : "HLCSG" : 1 =
176     [
177         1 : "Normal"
178         2 : "Onlyents"
179         0 : "Off"
180     ]
181     */
182     iTmp = IntForKey(mapent, "hlcsg");
183     g_onlyents = false;
184     if (iTmp == 2)
185     {
186         g_onlyents = true;
187     }
188     else if (iTmp == 0)
189     {
190         Fatal(assume_TOOL_CANCEL,
191             "%s was set to \"Off\" (0) in info_compile_parameters entity, execution cancelled", g_Program);
192         CheckFatal();
193     }
194     Log("%30s [ %-9s ]\n", "Onlyents", g_onlyents ? "on" : "off");
195 
196     /*
197     nocliphull(choices) : "Generate clipping hulls" : 0 =
198     [
199         0 : "Yes"
200         1 : "No"
201     ]
202     */
203     iTmp = IntForKey(mapent, "nocliphull");
204     if (iTmp == 1)
205     {
206         g_noclip = true;
207     }
208     else
209     {
210         g_noclip = false;
211     }
212     Log("%30s [ %-9s ]\n", "Clipping Hull Generation", g_noclip ? "off" : "on");
213 #ifdef HLCSG_PRECISIONCLIP
214     // cliptype(choices) : "Clip Hull Type" : 4 = [ 0 : "Smallest" 1 : "Normalized" 2: "Simple" 3 : "Precise" 4 : "Legacy" ]
215     iTmp = IntForKey(mapent, "cliptype");
216 	switch(iTmp)
217 	{
218 	case 0:
219 		g_cliptype = clip_smallest;
220 		break;
221 	case 1:
222 		g_cliptype = clip_normalized;
223 		break;
224 	case 2:
225 		g_cliptype = clip_simple;
226 		break;
227 	case 3:
228 		g_cliptype = clip_precise;
229 		break;
230 	default:
231 		g_cliptype = clip_legacy;
232 		break;
233 	}
234     Log("%30s [ %-9s ]\n", "Clip Hull Type", GetClipTypeString(g_cliptype));
235 #endif
236     /*
237     noskyclip(choices) : "No Sky Clip" : 0 =
238     [
239         1 : "On"
240         0 : "Off"
241     ]
242     */
243     iTmp = IntForKey(mapent, "noskyclip");
244     if (iTmp == 1)
245     {
246         g_skyclip = false;
247     }
248     else
249     {
250         g_skyclip = true;
251     }
252     Log("%30s [ %-9s ]\n", "Sky brush clip generation", g_skyclip ? "on" : "off");
253 
254     ///////////////
255     Log("\n");
256 }
257 #endif
258 
259 // =====================================================================================
260 // FixBevelTextures
261 // =====================================================================================
262 
FixBevelTextures()263 void FixBevelTextures()
264 {
265 	for(int counter = 0; counter < g_numtexinfo; counter++)
266 	{
267 		if(g_texinfo[counter].flags & TEX_BEVEL)
268 		{ g_texinfo[counter].flags &= ~TEX_BEVEL; }
269 	}
270 }
271 
272 // =====================================================================================
273 //  NewFaceFromFace
274 //      Duplicates the non point information of a face, used by SplitFace
275 // =====================================================================================
NewFaceFromFace(const bface_t * const in)276 bface_t*        NewFaceFromFace(const bface_t* const in)
277 {
278     bface_t*        newf;
279 
280     newf = (bface_t*)Alloc(sizeof(bface_t));
281 
282     newf->contents = in->contents;
283     newf->texinfo = in->texinfo;
284     newf->planenum = in->planenum;
285     newf->plane = in->plane;
286 
287     return newf;
288 }
289 
290 // =====================================================================================
291 //  FreeFace
292 // =====================================================================================
FreeFace(bface_t * f)293 void            FreeFace(bface_t* f)
294 {
295     delete f->w;
296     Free(f);
297 }
298 
299 // =====================================================================================
300 //  ClipFace
301 //      Clips a faces by a plane, returning the fragment on the backside and adding any
302 //      fragment to the outside.
303 //      Faces exactly on the plane will stay inside unless overdrawn by later brush.
304 //      Frontside is the side of the plane that holds the outside list.
305 //      Precedence is necesary to handle overlapping coplanar faces.
306 #define	SPLIT_EPSILON	0.3
307 // =====================================================================================
ClipFace(bface_t * f,bface_t ** outside,const int splitplane,const bool precedence)308 static bface_t* ClipFace(bface_t* f, bface_t** outside, const int splitplane, const bool precedence)
309 {
310     bface_t*        front;  // clip face
311     Winding*        fw;     // forward wind
312     Winding*        bw;     // back wind
313     plane_t*        split; // plane to clip on
314 
315     // handle exact plane matches special
316 
317     if (f->planenum == (splitplane ^ 1))
318         return f;    // opposite side, so put on inside list
319 
320     if (f->planenum == splitplane)  // coplanar
321     {
322         // this fragment will go to the inside, because
323         //   the earlier one was clipped to the outside
324         if (precedence)
325             return f;
326 
327         f->next = *outside;
328         *outside = f;
329         return NULL;
330     }
331 
332     split = &g_mapplanes[splitplane];
333     f->w->Clip(split->normal, split->dist, &fw, &bw);
334 
335     if (!fw)
336     {
337         delete bw;
338         return f;
339     }
340     else if (!bw)
341     {
342         delete fw;
343         f->next = *outside;
344         *outside = f;
345         return NULL;
346     }
347     else
348     {
349         delete f->w;
350 
351         front = NewFaceFromFace(f);
352         front->w = fw;
353         fw->getBounds(front->bounds);
354         front->next = *outside;
355         *outside = front;
356 
357         f->w = bw;
358         bw->getBounds(f->bounds);
359 
360         return f;
361     }
362 }
363 
364 // =====================================================================================
365 //  WriteFace
366 // =====================================================================================
WriteFace(const int hull,const bface_t * const f)367 void            WriteFace(const int hull, const bface_t* const f)
368 {
369     unsigned int    i;
370     Winding*        w;
371 
372     ThreadLock();
373     if (!hull)
374         c_csgfaces++;
375 
376     // .p0 format
377     w = f->w;
378 
379     // plane summary
380     fprintf(out[hull], "%i %i %i %u\n", f->planenum, f->texinfo, f->contents, w->m_NumPoints);
381 
382     // for each of the points on the face
383     for (i = 0; i < w->m_NumPoints; i++)
384     {
385         // write the co-ords
386         fprintf(out[hull], "%5.2f %5.2f %5.2f\n", w->m_Points[i][0], w->m_Points[i][1], w->m_Points[i][2]);
387     }
388 
389     // put in an extra line break
390     fprintf(out[hull], "\n");
391 
392     ThreadUnlock();
393 }
394 
395 // =====================================================================================
396 //  SaveOutside
397 //      The faces remaining on the outside list are final polygons.  Write them to the
398 //      output file.
399 //      Passable contents (water, lava, etc) will generate a mirrored copy of the face
400 //      to be seen from the inside.
401 // =====================================================================================
SaveOutside(const brush_t * const b,const int hull,bface_t * outside,const int mirrorcontents)402 static void     SaveOutside(const brush_t* const b, const int hull, bface_t* outside, const int mirrorcontents)
403 {
404     bface_t*        f;
405     bface_t*        f2;
406     bface_t*        next;
407     int             i;
408     vec3_t          temp;
409 
410     for (f = outside; f; f = next)
411     {
412         next = f->next;
413 
414         if (f->w->getArea() < g_tiny_threshold)
415         {
416             c_tiny++;
417             Verbose("Entity %i, Brush %i: tiny fragment\n", b->entitynum, b->brushnum);
418             continue;
419         }
420 
421         // count unique faces
422         if (!hull)
423         {
424             for (f2 = b->hulls[hull].faces; f2; f2 = f2->next)
425             {
426                 if (f2->planenum == f->planenum)
427                 {
428                     if (!f2->used)
429                     {
430                         f2->used = true;
431                         c_outfaces++;
432                     }
433                     break;
434                 }
435             }
436         }
437 
438         WriteFace(hull, f);
439 
440         //              if (mirrorcontents != CONTENTS_SOLID)
441         {
442             f->planenum ^= 1;
443             f->plane = &g_mapplanes[f->planenum];
444             f->contents = mirrorcontents;
445 
446             // swap point orders
447             for (i = 0; i < f->w->m_NumPoints / 2; i++)      // add points backwards
448             {
449                 VectorCopy(f->w->m_Points[i], temp);
450                 VectorCopy(f->w->m_Points[f->w->m_NumPoints - 1 - i], f->w->m_Points[i]);
451                 VectorCopy(temp, f->w->m_Points[f->w->m_NumPoints - 1 - i]);
452             }
453             WriteFace(hull, f);
454         }
455 
456         FreeFace(f);
457     }
458 }
459 
460 // =====================================================================================
461 //  CopyFace
462 // =====================================================================================
CopyFace(const bface_t * const f)463 bface_t*        CopyFace(const bface_t* const f)
464 {
465     bface_t*        n;
466 
467     n = NewFaceFromFace(f);
468     n->w = f->w->Copy();
469     n->bounds = f->bounds;
470     return n;
471 }
472 
473 // =====================================================================================
474 //  CopyFaceList
475 // =====================================================================================
CopyFaceList(bface_t * f)476 bface_t*        CopyFaceList(bface_t* f)
477 {
478     bface_t*        head;
479     bface_t*        n;
480 
481     if (f)
482     {
483         head = CopyFace(f);
484         n = head;
485         f = f->next;
486 
487         while (f)
488         {
489             n->next = CopyFace(f);
490 
491             n = n->next;
492             f = f->next;
493         }
494 
495         return head;
496     }
497     else
498     {
499         return NULL;
500     }
501 }
502 
503 // =====================================================================================
504 //  FreeFaceList
505 // =====================================================================================
FreeFaceList(bface_t * f)506 void            FreeFaceList(bface_t* f)
507 {
508     if (f)
509     {
510         if (f->next)
511         {
512             FreeFaceList(f->next);
513         }
514         FreeFace(f);
515     }
516 }
517 
518 // =====================================================================================
519 //  CopyFacesToOutside
520 //      Make a copy of all the faces of the brush, so they can be chewed up by other
521 //      brushes.
522 //      All of the faces start on the outside list.
523 //      As other brushes take bites out of the faces, the fragments are moved to the
524 //      inside list, so they can be freed when they are determined to be completely
525 //      enclosed in solid.
526 // =====================================================================================
CopyFacesToOutside(brushhull_t * bh)527 static bface_t* CopyFacesToOutside(brushhull_t* bh)
528 {
529     bface_t*        f;
530     bface_t*        newf;
531     bface_t*        outside;
532 
533     outside = NULL;
534 
535     for (f = bh->faces; f; f = f->next)
536     {
537         newf = CopyFace(f);
538         newf->w->getBounds(newf->bounds);
539         newf->next = outside;
540         outside = newf;
541     }
542 
543     return outside;
544 }
545 
546 // =====================================================================================
547 //  CSGBrush
548 // =====================================================================================
CSGBrush(intptr_t brushnum)549 static void     CSGBrush(intptr_t brushnum)
550 {
551     int             hull;
552     brush_t*        b1;
553     brush_t*        b2;
554     brushhull_t*    bh1;
555     brushhull_t*    bh2;
556     int             bn;
557     bool            overwrite;
558     bface_t*        f;
559     bface_t*        f2;
560     bface_t*        next;
561     bface_t*        fcopy;
562     bface_t*        outside;
563     bface_t*        oldoutside;
564     entity_t*       e;
565     vec_t           area;
566 
567     // get entity and brush info from the given brushnum that we can work with
568     b1 = &g_mapbrushes[brushnum];
569     e = &g_entities[b1->entitynum];
570 
571     // for each of the hulls
572     for (hull = 0; hull < NUM_HULLS; hull++)
573     {
574         bh1 = &b1->hulls[hull];
575 
576         // set outside to a copy of the brush's faces
577         outside = CopyFacesToOutside(bh1);
578         overwrite = false;
579 
580         // for each brush in entity e
581         for (bn = 0; bn < e->numbrushes; bn++)
582         {
583             // see if b2 needs to clip a chunk out of b1
584             if (bn == brushnum)
585             {
586                 overwrite = true;                          // later brushes now overwrite
587                 continue;
588             }
589 
590             b2 = &g_mapbrushes[e->firstbrush + bn];
591             bh2 = &b2->hulls[hull];
592 
593             if (!bh2->faces)
594                 continue;                                  // brush isn't in this hull
595 
596             // check brush bounding box first
597             // TODO: use boundingbox method instead
598             if (bh1->bounds.testDisjoint(bh2->bounds))
599             {
600                 continue;
601             }
602 
603             // divide faces by the planes of the b2 to find which
604             // fragments are inside
605 
606             f = outside;
607             outside = NULL;
608             for (; f; f = next)
609             {
610                 next = f->next;
611 
612                 // check face bounding box first
613                 if (bh2->bounds.testDisjoint(f->bounds))
614                 {                                          // this face doesn't intersect brush2's bbox
615                     f->next = outside;
616                     outside = f;
617                     continue;
618                 }
619 
620                 oldoutside = outside;
621                 fcopy = CopyFace(f);                       // save to avoid fake splits
622 
623                 // throw pieces on the front sides of the planes
624                 // into the outside list, return the remains on the inside
625                 for (f2 = bh2->faces; f2 && f; f2 = f2->next)
626                 {
627                     f = ClipFace(f, &outside, f2->planenum, overwrite);
628                 }
629 
630                 area = f ? f->w->getArea() : 0;
631                 if (f && area < g_tiny_threshold)
632                 {
633                     Verbose("Entity %i, Brush %i: tiny penetration\n", b1->entitynum, b1->brushnum);
634                     c_tiny_clip++;
635                     FreeFace(f);
636                     f = NULL;
637                 }
638                 if (f)
639                 {
640                     // there is one convex fragment of the original
641                     // face left inside brush2
642                     FreeFace(fcopy);
643 
644                     if (b1->contents > b2->contents)
645                     {                                      // inside a water brush
646                         f->contents = b2->contents;
647                         f->next = outside;
648                         outside = f;
649                     }
650                     else                                   // inside a solid brush
651                     {
652                         FreeFace(f);                       // throw it away
653                     }
654                 }
655                 else
656                 {                                          // the entire thing was on the outside, even
657                     // though the bounding boxes intersected,
658                     // which will never happen with axial planes
659 
660                     // free the fragments chopped to the outside
661                     while (outside != oldoutside)
662                     {
663                         f2 = outside->next;
664                         FreeFace(outside);
665                         outside = f2;
666                     }
667 
668                     // revert to the original face to avoid
669                     // unneeded false cuts
670                     fcopy->next = outside;
671                     outside = fcopy;
672                 }
673             }
674 
675         }
676 
677         // all of the faces left in outside are real surface faces
678         SaveOutside(b1, hull, outside, b1->contents);
679     }
680 }
681 
682 //
683 // =====================================================================================
684 //
685 
686 // =====================================================================================
687 //  EmitPlanes
688 // =====================================================================================
EmitPlanes()689 static void     EmitPlanes()
690 {
691     int             i;
692     dplane_t*       dp;
693     plane_t*        mp;
694 
695     g_numplanes = g_nummapplanes;
696     mp = g_mapplanes;
697     dp = g_dplanes;
698     for (i = 0; i < g_nummapplanes; i++, mp++, dp++)
699     {
700         //if (!(mp->redundant))
701         //{
702         //    Log("EmitPlanes: plane %i non redundant\n", i);
703             VectorCopy(mp->normal, dp->normal);
704             dp->dist = mp->dist;
705             dp->type = mp->type;
706        // }
707         //else
708        // {
709        //     Log("EmitPlanes: plane %i redundant\n", i);
710        // }
711     }
712 }
713 
714 // =====================================================================================
715 //  SetModelNumbers
716 //      blah
717 // =====================================================================================
SetModelNumbers()718 static void     SetModelNumbers()
719 {
720     int             i;
721     int             models;
722     char            value[10];
723 
724     models = 1;
725     for (i = 1; i < g_numentities; i++)
726     {
727         if (g_entities[i].numbrushes)
728         {
729             safe_snprintf(value, sizeof(value), "*%i", models);
730             models++;
731             SetKeyValue(&g_entities[i], "model", value);
732         }
733     }
734 }
735 
736 // =====================================================================================
737 //  SetLightStyles
738 // =====================================================================================
739 #define	MAX_SWITCHED_LIGHTS	    32
740 #define MAX_LIGHTTARGETS_NAME   64
741 
SetLightStyles()742 static void     SetLightStyles()
743 {
744     int             stylenum;
745     const char*     t;
746     entity_t*       e;
747     int             i, j;
748     char            value[10];
749     char            lighttargets[MAX_SWITCHED_LIGHTS][MAX_LIGHTTARGETS_NAME];
750 
751 #ifdef ZHLT_TEXLIGHT
752     	bool			newtexlight = false;
753 #endif
754 
755     // any light that is controlled (has a targetname)
756     // must have a unique style number generated for it
757 
758     stylenum = 0;
759     for (i = 1; i < g_numentities; i++)
760     {
761         e = &g_entities[i];
762 
763         t = ValueForKey(e, "classname");
764         if (strncasecmp(t, "light", 5))
765         {
766 #ifdef ZHLT_TEXLIGHT
767             //LRC:
768 			// if it's not a normal light entity, allocate it a new style if necessary.
769 	        t = ValueForKey(e, "style");
770 			switch (atoi(t))
771 			{
772 			case 0: // not a light, no style, generally pretty boring
773 				continue;
774 			case -1: // normal switchable texlight
775 				safe_snprintf(value, sizeof(value), "%i", 32 + stylenum);
776 				SetKeyValue(e, "style", value);
777 				stylenum++;
778 				continue;
779 			case -2: // backwards switchable texlight
780 				safe_snprintf(value, sizeof(value), "%i", -(32 + stylenum));
781 				SetKeyValue(e, "style", value);
782 				stylenum++;
783 				continue;
784 			case -3: // (HACK) a piggyback texlight: switched on and off by triggering a real light that has the same name
785 				SetKeyValue(e, "style", "0"); // just in case the level designer didn't give it a name
786 				newtexlight = true;
787 				// don't 'continue', fall out
788 			}
789 	        //LRC (ends)
790 #else
791             continue;
792 #endif
793         }
794         t = ValueForKey(e, "targetname");
795         if (!t[0])
796         {
797             continue;
798         }
799 
800         // find this targetname
801         for (j = 0; j < stylenum; j++)
802         {
803             if (!strcmp(lighttargets[j], t))
804             {
805                 break;
806             }
807         }
808         if (j == stylenum)
809         {
810             hlassume(stylenum < MAX_SWITCHED_LIGHTS, assume_MAX_SWITCHED_LIGHTS);
811             safe_strncpy(lighttargets[j], t, MAX_LIGHTTARGETS_NAME);
812             stylenum++;
813         }
814         safe_snprintf(value, sizeof(value), "%i", 32 + j);
815         SetKeyValue(e, "style", value);
816     }
817 
818 }
819 
820 // =====================================================================================
821 //  ConvertHintToEmtpy
822 // =====================================================================================
ConvertHintToEmpty()823 static void     ConvertHintToEmpty()
824 {
825     int             i;
826 
827     // Convert HINT brushes to EMPTY after they have been carved by csg
828     for (i = 0; i < MAX_MAP_BRUSHES; i++)
829     {
830         if (g_mapbrushes[i].contents == CONTENTS_HINT)
831         {
832             g_mapbrushes[i].contents = CONTENTS_EMPTY;
833         }
834     }
835 }
836 
837 // =====================================================================================
838 //  WriteBSP
839 // =====================================================================================
WriteBSP(const char * const name)840 void WriteBSP(const char* const name)
841 {
842     char path[_MAX_PATH];
843 
844     safe_strncpy(path, name, _MAX_PATH);
845     DefaultExtension(path, ".bsp");
846 
847     SetModelNumbers();
848     SetLightStyles();
849 
850     if (!g_onlyents)
851         WriteMiptex();
852 
853     UnparseEntities();
854     ConvertHintToEmpty();
855     WriteBSPFile(path);
856 }
857 
858 //
859 // =====================================================================================
860 //
861 
862 // AJM: added in function
863 // =====================================================================================
864 //  CopyGenerictoCLIP
865 //      clips a generic brush
866 // =====================================================================================
CopyGenerictoCLIP(const brush_t * const b)867 static void     CopyGenerictoCLIP(const brush_t* const b)
868 {
869     // code blatently ripped from CopySKYtoCLIP()
870 
871     int             i;
872     entity_t*       mapent;
873     brush_t*        newbrush;
874 
875     mapent = &g_entities[b->entitynum];
876     mapent->numbrushes++;
877 
878     newbrush = &g_mapbrushes[g_nummapbrushes];
879     newbrush->entitynum = b->entitynum;
880     newbrush->brushnum = g_nummapbrushes - mapent->firstbrush;
881     newbrush->firstside = g_numbrushsides;
882     newbrush->numsides = b->numsides;
883     newbrush->contents = CONTENTS_CLIP;
884     newbrush->noclip = 0;
885 
886     for (i = 0; i < b->numsides; i++)
887     {
888         int             j;
889 
890         side_t*         side = &g_brushsides[g_numbrushsides];
891 
892         *side = g_brushsides[b->firstside + i];
893         safe_strncpy(side->td.name, "CLIP", sizeof(side->td.name));
894 
895         for (j = 0; j < NUM_HULLS; j++)
896         {
897             newbrush->hulls[j].faces = NULL;
898             newbrush->hulls[j].bounds = b->hulls[j].bounds;
899         }
900 
901         g_numbrushsides++;
902         hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
903     }
904 
905     g_nummapbrushes++;
906     hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
907 }
908 
909 #ifdef HLCSG_CLIPECONOMY
910 // AJM: added in
911 unsigned int    BrushClipHullsDiscarded = 0;
912 unsigned int    ClipNodesDiscarded = 0;
913 
914 //AJM: added in function
MarkEntForNoclip(entity_t * ent)915 static void     MarkEntForNoclip(entity_t*  ent)
916 {
917     int             i;
918     brush_t*        b;
919 
920     for (i = ent->firstbrush; i < ent->firstbrush + ent->numbrushes; i++)
921     {
922         b = &g_mapbrushes[i];
923         b->noclip = 1;
924 
925         BrushClipHullsDiscarded++;
926         ClipNodesDiscarded += b->numsides;
927     }
928 }
929 
930 // AJM
931 // =====================================================================================
932 //  CheckForNoClip
933 //      marks the noclip flag on any brushes that dont need clipnode generation, eg. func_illusionaries
934 // =====================================================================================
CheckForNoClip()935 static void     CheckForNoClip()
936 {
937     int             i;
938     entity_t*       ent;
939 
940     char            entclassname[MAX_KEY];
941     int             spawnflags;
942 
943     if (!g_bClipNazi)
944         return; // NO CLIP FOR YOU!!!
945 
946     for (i = 0; i < g_numentities; i++)
947     {
948         if (!g_entities[i].numbrushes)
949             continue; // not a model
950 
951         if (!i)
952             continue; // dont waste our time with worldspawn
953 
954         ent = &g_entities[i];
955 
956         strcpy_s(entclassname, ValueForKey(ent, "classname"));
957         spawnflags = atoi(ValueForKey(ent, "spawnflags"));
958 
959 		// condition 0, it's marked noclip (KGP)
960 		if(strlen(ValueForKey(ent,"zhlt_noclip")) && strcmp(ValueForKey(ent,"zhlt_noclip"),"0"))
961 		{
962 			MarkEntForNoclip(ent);
963 		}
964         // condition 1, its a func_illusionary
965 		else if (!strncasecmp(entclassname,      "func_illusionary", 16))
966         {
967             MarkEntForNoclip(ent);
968         }
969         // condition 2, flag 4 (8) is set and it is either a func_door, func_train, momentary_door,
970         //  func_door_rotating or func_tracktrain (passable, not-solid flag )
971         else if (    (spawnflags & 8)
972                      &&
973                      (   /* NOTE: func_doors as far as i can tell may need clipnodes for their
974                             player collision detection, so for now, they stay out of it. */
975                           (!strncasecmp(entclassname, "func_train",         10))
976                        || (!strncasecmp(entclassname, "func_door",           9))
977                   //   || (!strncasecmp(entclassname, "momentary_door",     14))
978                   //   || (!strncasecmp(entclassname, "func_door_rotating", 18))
979                        || (!strncasecmp(entclassname, "func_tracktrain",    15))
980                      )
981                 )
982         {
983             MarkEntForNoclip(ent);
984         }
985         // condition 3: flag 2 (2) is set, and its a func_conveyor (not solid flag)
986         else if ( (spawnflags & 2) && (!strncasecmp(entclassname, "func_conveyor", 13)) )
987         {
988             MarkEntForNoclip(ent);
989         }
990         // condition 4: flag 1 (1) is set, and its a func_rot_button (not solid flag)
991         else if ( (spawnflags & 1) && (!strncasecmp(entclassname, "func_rot_button", 15)) )
992         {
993             MarkEntForNoclip(ent);
994         }
995         // condition 5: flag 7 (64) is set, and its a func_rotating
996         else if ( (spawnflags & 64) && (!strncasecmp(entclassname, "func_rotating", 13)) )
997         {
998             MarkEntForNoclip(ent);
999         }
1000         /*
1001         // condition 6: its a func_wall, while we noclip it, we remake the clipnodes manually
1002         else if (!strncasecmp(entclassname, "func_wall", 9))
1003         {
1004             for (int j = ent->firstbrush; j < ent->firstbrush + ent->numbrushes; j++)
1005                 CopyGenerictoCLIP(&g_mapbrushes[i]);
1006 
1007             MarkEntForNoclip(ent);
1008         }
1009 */
1010     }
1011 
1012     Log("%i brushes (totalling %i sides) discarded from clipping hulls\n", BrushClipHullsDiscarded, ClipNodesDiscarded);
1013 }
1014 #endif
1015 
1016 // =====================================================================================
1017 //  ProcessModels
1018 // =====================================================================================
1019 #define NUM_TYPECONTENTS    5 // AJM: should reflect the number of values below
1020 int typecontents[NUM_TYPECONTENTS] = {
1021     CONTENTS_WATER, CONTENTS_SLIME, CONTENTS_LAVA, CONTENTS_SKY, CONTENTS_HINT
1022 };
1023 
1024 
ProcessModels()1025 static void     ProcessModels()
1026 {
1027     int             i, j, type;
1028     int             placed;
1029     int             first, contents;
1030     brush_t         temp;
1031 
1032     for (i = 0; i < g_numentities; i++)
1033     {
1034         if (!g_entities[i].numbrushes) // only models
1035             continue;
1036 
1037         // sort the contents down so stone bites water, etc
1038         first = g_entities[i].firstbrush;
1039         placed = 0;
1040         for (type = 0; type < NUM_TYPECONTENTS; type++)                 // for each of the contents types
1041         {
1042             contents = typecontents[type];
1043             for (j = placed + 1; j < g_entities[i].numbrushes; j++)     // for each of the model's brushes
1044             {
1045                 // if this brush is of the contents type in this for iteration
1046                 if (g_mapbrushes[first + j].contents == contents)
1047                 {
1048                     temp = g_mapbrushes[first + placed];
1049                     g_mapbrushes[first + placed] = g_mapbrushes[j];
1050                     g_mapbrushes[j] = temp;
1051                     placed++;
1052                 }
1053             }
1054         }
1055 
1056         // csg them in order
1057         if (i == 0) // if its worldspawn....
1058         {
1059             NamedRunThreadsOnIndividual(g_entities[i].numbrushes, g_estimate, CSGBrush);
1060             CheckFatal();
1061         }
1062         else
1063         {
1064             for (j = 0; j < g_entities[i].numbrushes; j++)
1065             {
1066                 CSGBrush(first + j);
1067             }
1068         }
1069 
1070         // write end of model marker
1071         for (j = 0; j < NUM_HULLS; j++)
1072         {
1073             fprintf(out[j], "-1 -1 -1 -1\n");
1074         }
1075     }
1076 }
1077 
1078 // =====================================================================================
1079 //  SetModelCenters
1080 // =====================================================================================
SetModelCenters(intptr_t entitynum)1081 static void     SetModelCenters(intptr_t entitynum)
1082 {
1083     int             i;
1084     int             last;
1085     char            string[MAXTOKEN];
1086     entity_t*       e = &g_entities[entitynum];
1087     BoundingBox     bounds;
1088     vec3_t          center;
1089 
1090     if ((entitynum == 0) || (e->numbrushes == 0)) // skip worldspawn and point entities
1091         return;
1092 
1093     if (!*ValueForKey(e, "light_origin")) // skip if its not a zhlt_flags light_origin
1094         return;
1095 
1096     for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++)
1097     {
1098         if (g_mapbrushes[i].contents != CONTENTS_ORIGIN)
1099         {
1100             bounds.add(g_mapbrushes[i].hulls->bounds);
1101         }
1102     }
1103 
1104     VectorAdd(bounds.m_Mins, bounds.m_Maxs, center);
1105     VectorScale(center, 0.5, center);
1106 
1107     safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)center[0], (int)center[1], (int)center[2]);
1108     SetKeyValue(e, "model_center", string);
1109 }
1110 
1111 //
1112 // =====================================================================================
1113 //
1114 
1115 // =====================================================================================
1116 //  BoundWorld
1117 // =====================================================================================
BoundWorld()1118 static void     BoundWorld()
1119 {
1120     int             i;
1121     brushhull_t*    h;
1122 
1123     world_bounds.reset();
1124 
1125     for (i = 0; i < g_nummapbrushes; i++)
1126     {
1127         h = &g_mapbrushes[i].hulls[0];
1128         if (!h->faces)
1129         {
1130             continue;
1131         }
1132         world_bounds.add(h->bounds);
1133     }
1134 
1135     Verbose("World bounds: (%i %i %i) to (%i %i %i)\n",
1136             (int)world_bounds.m_Mins[0], (int)world_bounds.m_Mins[1], (int)world_bounds.m_Mins[2],
1137             (int)world_bounds.m_Maxs[0], (int)world_bounds.m_Maxs[1], (int)world_bounds.m_Maxs[2]);
1138 }
1139 
1140 // =====================================================================================
1141 //  Usage
1142 //      prints out usage sheet
1143 // =====================================================================================
Usage()1144 static void     Usage()
1145 {
1146     Banner(); // TODO: Call banner from main CSG process?
1147 
1148     Log("\n-= %s Options =-\n\n", g_Program);
1149     Log("    -nowadtextures   : include all used textures into bsp\n");
1150     Log("    -wadinclude file : place textures used from wad specified into bsp\n");
1151     Log("    -noclip          : don't create clipping hull\n");
1152 
1153 #ifdef HLCSG_CLIPECONOMY    // AJM
1154     Log("    -noclipeconomy   : turn clipnode economy mode off\n");
1155 #endif
1156 
1157 #ifdef HLCSG_PRECISIONCLIP // KGP
1158 	Log("    -cliptype value  : set to smallest, normalized, simple, precise, or legacy (default)\n");
1159 #endif
1160 #ifdef HLCSG_NULLIFY_INVISIBLE // KGP
1161 	Log("    -nullfile file   : specify list of entities to retexture with NULL\n");
1162 #endif
1163 
1164     Log("    -onlyents        : do an entity update from .map to .bsp\n");
1165     Log("    -noskyclip       : disable automatic clipping of SKY brushes\n");
1166     Log("    -tiny #          : minmum brush face surface area before it is discarded\n");
1167     Log("    -brushunion #    : threshold to warn about overlapping brushes\n\n");
1168     Log("    -hullfile file   : Reads in custom collision hull dimensions\n");
1169     Log("    -texdata #       : Alter maximum texture memory limit (in kb)\n");
1170     Log("    -lightdata #     : Alter maximum lighting memory limit (in kb)\n");
1171     Log("    -chart           : display bsp statitics\n");
1172     Log("    -low | -high     : run program an altered priority level\n");
1173     Log("    -nolog           : don't generate the compile logfiles\n");
1174     Log("    -threads #       : manually specify the number of threads to run\n");
1175 #ifdef SYSTEM_WIN32
1176     Log("    -estimate        : display estimated time during compile\n");
1177 #endif
1178 #ifdef ZHLT_PROGRESSFILE // AJM
1179     Log("    -progressfile path  : specify the path to a file for progress estimate output\n");
1180 #endif
1181 #ifdef SYSTEM_POSIX
1182     Log("    -noestimate      : do not display continuous compile time estimates\n");
1183 #endif
1184     Log("    -verbose         : compile with verbose messages\n");
1185     Log("    -noinfo          : Do not show tool configuration information\n");
1186 
1187 #ifdef ZHLT_NULLTEX // AJM
1188     Log("    -nonulltex       : Turns off null texture stripping\n");
1189 #endif
1190 
1191 #ifdef ZHLT_DETAIL // AJM
1192     Log("    -nodetail        : dont handle detail brushes\n");
1193 #endif
1194 
1195     Log("    -dev #           : compile with developer message\n\n");
1196 
1197 #ifdef HLCSG_WADCFG // AJM
1198     Log("    -wadconfig name  : Specify a configuration to use from wad.cfg\n");
1199     Log("    -wadcfgfile path : Manually specify a path to the wad.cfg file\n"); //JK:
1200 #endif
1201 
1202 #ifdef HLCSG_AUTOWAD // AJM:
1203     Log("    -wadautodetect   : Force auto-detection of wadfiles\n");
1204 #endif
1205     Log("    mapfile          : The mapfile to compile\n\n");
1206 
1207     exit(1);
1208 }
1209 
1210 // =====================================================================================
1211 //  DumpWadinclude
1212 //      prints out the wadinclude list
1213 // =====================================================================================
DumpWadinclude()1214 static void     DumpWadinclude()
1215 {
1216     Log("Wadinclude list :\n");
1217     WadInclude_i it;
1218     for (it = g_WadInclude.begin(); it != g_WadInclude.end(); it++)
1219     {
1220         Log("[%s]\n", it->c_str());
1221     }
1222 }
1223 
1224 // =====================================================================================
1225 //  Settings
1226 //      prints out settings sheet
1227 // =====================================================================================
Settings()1228 static void     Settings()
1229 {
1230     char*           tmp;
1231 
1232     if (!g_info)
1233         return;
1234 
1235     Log("\nCurrent %s Settings\n", g_Program);
1236     Log("Name                 |  Setting  |  Default\n"
1237         "---------------------|-----------|-------------------------\n");
1238 
1239     // ZHLT Common Settings
1240     if (DEFAULT_NUMTHREADS == -1)
1241     {
1242         Log("threads               [ %7d ] [  Varies ]\n", g_numthreads);
1243     }
1244     else
1245     {
1246         Log("threads               [ %7d ] [ %7d ]\n", g_numthreads, DEFAULT_NUMTHREADS);
1247     }
1248 
1249     Log("verbose               [ %7s ] [ %7s ]\n", g_verbose ? "on" : "off", DEFAULT_VERBOSE ? "on" : "off");
1250     Log("log                   [ %7s ] [ %7s ]\n", g_log ? "on" : "off", DEFAULT_LOG ? "on" : "off");
1251 
1252     Log("developer             [ %7d ] [ %7d ]\n", g_developer, DEFAULT_DEVELOPER);
1253     Log("chart                 [ %7s ] [ %7s ]\n", g_chart ? "on" : "off", DEFAULT_CHART ? "on" : "off");
1254     Log("estimate              [ %7s ] [ %7s ]\n", g_estimate ? "on" : "off", DEFAULT_ESTIMATE ? "on" : "off");
1255     Log("max texture memory    [ %7d ] [ %7d ]\n", g_max_map_miptex, DEFAULT_MAX_MAP_MIPTEX);
1256 	Log("max lighting memory   [ %7d ] [ %7d ]\n", g_max_map_lightdata, DEFAULT_MAX_MAP_LIGHTDATA);
1257 
1258     switch (g_threadpriority)
1259     {
1260     case eThreadPriorityNormal:
1261     default:
1262         tmp = "Normal";
1263         break;
1264     case eThreadPriorityLow:
1265         tmp = "Low";
1266         break;
1267     case eThreadPriorityHigh:
1268         tmp = "High";
1269         break;
1270     }
1271     Log("priority              [ %7s ] [ %7s ]\n", tmp, "Normal");
1272     Log("\n");
1273 
1274     // HLCSG Specific Settings
1275 
1276     Log("noclip                [ %7s ] [ %7s ]\n", g_noclip          ? "on" : "off", DEFAULT_NOCLIP       ? "on" : "off");
1277 
1278 #ifdef ZHLT_NULLTEX // AJM:
1279     Log("null texture stripping[ %7s ] [ %7s ]\n", g_bUseNullTex     ? "on" : "off", DEFAULT_NULLTEX      ? "on" : "off");
1280 #endif
1281 
1282 #ifdef ZHLT_DETAIL // AJM
1283     Log("detail brushes        [ %7s ] [ %7s ]\n", g_bDetailBrushes  ? "on" : "off", DEFAULT_DETAIL       ? "on" : "off");
1284 #endif
1285 
1286 #ifdef HLCSG_CLIPECONOMY // AJM
1287     Log("clipnode economy mode [ %7s ] [ %7s ]\n", g_bClipNazi       ? "on" : "off", DEFAULT_CLIPNAZI     ? "on" : "off");
1288 #endif
1289 
1290 #ifdef HLCSG_PRECISIONCLIP // KGP
1291 	Log("clip hull type        [ %7s ] [ %7s ]\n", GetClipTypeString(g_cliptype), GetClipTypeString(DEFAULT_CLIPTYPE));
1292 #endif
1293 
1294     Log("onlyents              [ %7s ] [ %7s ]\n", g_onlyents        ? "on" : "off", DEFAULT_ONLYENTS     ? "on" : "off");
1295     Log("wadtextures           [ %7s ] [ %7s ]\n", g_wadtextures     ? "on" : "off", DEFAULT_WADTEXTURES  ? "on" : "off");
1296     Log("skyclip               [ %7s ] [ %7s ]\n", g_skyclip         ? "on" : "off", DEFAULT_SKYCLIP      ? "on" : "off");
1297     Log("hullfile              [ %7s ] [ %7s ]\n", g_hullfile ? g_hullfile : "None", "None");
1298 #ifdef HLCSG_NULLIFY_INVISIBLE // KGP
1299 	Log("nullfile              [ %7s ] [ %7s ]\n", g_nullfile ? g_nullfile : "None", "None");
1300 #endif
1301     // calc min surface area
1302     {
1303         char            tiny_penetration[10];
1304         char            default_tiny_penetration[10];
1305 
1306         safe_snprintf(tiny_penetration, sizeof(tiny_penetration), "%3.3f", g_tiny_threshold);
1307         safe_snprintf(default_tiny_penetration, sizeof(default_tiny_penetration), "%3.3f", DEFAULT_TINY_THRESHOLD);
1308         Log("min surface area      [ %7s ] [ %7s ]\n", tiny_penetration, default_tiny_penetration);
1309     }
1310 
1311     // calc union threshold
1312     {
1313         char            brush_union[10];
1314         char            default_brush_union[10];
1315 
1316         safe_snprintf(brush_union, sizeof(brush_union), "%3.3f", g_BrushUnionThreshold);
1317         safe_snprintf(default_brush_union, sizeof(default_brush_union), "%3.3f", DEFAULT_BRUSH_UNION_THRESHOLD);
1318         Log("brush union threshold [ %7s ] [ %7s ]\n", brush_union, default_brush_union);
1319     }
1320 
1321     Log("\n");
1322 }
1323 
1324 // AJM: added in
1325 // =====================================================================================
1326 //  CSGCleanup
1327 // =====================================================================================
CSGCleanup()1328 void            CSGCleanup()
1329 {
1330     //Log("CSGCleanup\n");
1331 #ifdef HLCSG_AUTOWAD
1332     autowad_cleanup();
1333 #endif
1334 #ifdef HLCSG_WADCFG
1335     WadCfg_cleanup();
1336 #endif
1337 #ifdef HLCSG_NULLIFY_TEXTURES
1338 	properties_cleanup();
1339 #endif
1340     FreeWadPaths();
1341 }
1342 
1343 // =====================================================================================
1344 //  Main
1345 //      Oh, come on.
1346 // =====================================================================================
main(const int argc,char ** argv)1347 int             main(const int argc, char** argv)
1348 {
1349     int             i;
1350     char            name[_MAX_PATH];            // mapanme
1351     double          start, end;                 // start/end time log
1352     const char*     mapname_from_arg = NULL;    // mapname path from passed argvar
1353 
1354     g_Program = "hlcsg";
1355 
1356     if (argc == 1)
1357         Usage();
1358 
1359     // Hard coded list of -wadinclude files, used for HINT texture brushes so lazy
1360     // mapmakers wont cause beta testers (or possibly end users) to get a wad
1361     // error on zhlt.wad etc
1362     g_WadInclude.push_back("zhlt.wad");
1363 
1364     memset(wadconfigname, 0, sizeof(wadconfigname));//AJM
1365 
1366     // detect argv
1367     for (i = 1; i < argc; i++)
1368     {
1369         if (!strcasecmp(argv[i], "-threads"))
1370         {
1371             if (i < argc)
1372             {
1373                 g_numthreads = atoi(argv[++i]);
1374                 if (g_numthreads < 1)
1375                 {
1376                     Log("Expected value of at least 1 for '-threads'\n");
1377                     Usage();
1378                 }
1379             }
1380             else
1381             {
1382                 Usage();
1383             }
1384         }
1385 
1386 #ifdef SYSTEM_WIN32
1387         else if (!strcasecmp(argv[i], "-estimate"))
1388         {
1389             g_estimate = true;
1390         }
1391 #endif
1392 
1393 #ifdef SYSTEM_POSIX
1394         else if (!strcasecmp(argv[i], "-noestimate"))
1395         {
1396             g_estimate = false;
1397         }
1398 #endif
1399 
1400         else if (!strcasecmp(argv[i], "-dev"))
1401         {
1402             if (i < argc)
1403             {
1404                 g_developer = (developer_level_t)atoi(argv[++i]);
1405             }
1406             else
1407             {
1408                 Usage();
1409             }
1410         }
1411         else if (!strcasecmp(argv[i], "-verbose"))
1412         {
1413             g_verbose = true;
1414         }
1415         else if (!strcasecmp(argv[i], "-noinfo"))
1416         {
1417             g_info = false;
1418         }
1419         else if (!strcasecmp(argv[i], "-chart"))
1420         {
1421             g_chart = true;
1422         }
1423         else if (!strcasecmp(argv[i], "-low"))
1424         {
1425             g_threadpriority = eThreadPriorityLow;
1426         }
1427         else if (!strcasecmp(argv[i], "-high"))
1428         {
1429             g_threadpriority = eThreadPriorityHigh;
1430         }
1431         else if (!strcasecmp(argv[i], "-nolog"))
1432         {
1433             g_log = false;
1434         }
1435         else if (!strcasecmp(argv[i], "-skyclip"))
1436         {
1437             g_skyclip = true;
1438         }
1439         else if (!strcasecmp(argv[i], "-noskyclip"))
1440         {
1441             g_skyclip = false;
1442         }
1443         else if (!strcasecmp(argv[i], "-noclip"))
1444         {
1445             g_noclip = true;
1446         }
1447         else if (!strcasecmp(argv[i], "-onlyents"))
1448         {
1449             g_onlyents = true;
1450         }
1451 
1452 #ifdef ZHLT_NULLTEX  // AJM: added in -nonulltex
1453         else if (!strcasecmp(argv[i], "-nonulltex"))
1454         {
1455             g_bUseNullTex = false;
1456         }
1457 #endif
1458 
1459 #ifdef HLCSG_CLIPECONOMY    // AJM: added in -noclipeconomy
1460         else if (!strcasecmp(argv[i], "-noclipeconomy"))
1461         {
1462             g_bClipNazi = false;
1463         }
1464 #endif
1465 
1466 #ifdef HLCSG_PRECISIONCLIP	// KGP: added in -cliptype
1467 		else if (!strcasecmp(argv[i], "-cliptype"))
1468 		{
1469 			if (i < argc)
1470 			{
1471 				++i;
1472 				if(!strcasecmp(argv[i],"smallest"))
1473 				{ g_cliptype = clip_smallest; }
1474 				else if(!strcasecmp(argv[i],"normalized"))
1475 				{ g_cliptype = clip_normalized; }
1476 				else if(!strcasecmp(argv[i],"simple"))
1477 				{ g_cliptype = clip_simple; }
1478 				else if(!strcasecmp(argv[i],"precise"))
1479 				{ g_cliptype = clip_precise; }
1480 				else if(!strcasecmp(argv[i],"legacy"))
1481 				{ g_cliptype = clip_legacy; }
1482 			}
1483             else
1484             {
1485                 Log("Error: -cliptype: incorrect usage of parameter\n");
1486                 Usage();
1487             }
1488 		}
1489 #endif
1490 
1491 #ifdef HLCSG_WADCFG
1492         // AJM: added in -wadconfig
1493         else if (!strcasecmp(argv[i], "-wadconfig"))
1494         {
1495             if (i < argc)
1496             {
1497                 safe_strncpy(wadconfigname, argv[++i], MAX_WAD_CFG_NAME);
1498                 if (strlen(argv[i]) > MAX_WAD_CFG_NAME)
1499                 {
1500                     Warning("wad configuration name was truncated to %i chars", MAX_WAD_CFG_NAME);
1501                     wadconfigname[MAX_WAD_CFG_NAME] = 0;
1502                 }
1503             }
1504             else
1505             {
1506                 Log("Error: -wadconfig: incorrect usage of parameter\n");
1507                 Usage();
1508             }
1509         }
1510 
1511         //JK: added in -wadcfgfile
1512         else if (!strcasecmp(argv[i], "-wadcfgfile"))
1513         {
1514             if (i < argc)
1515             {
1516                 g_wadcfgfile = argv[++i];
1517             }
1518             else
1519             {
1520             	Log("Error: -wadcfgfile: incorrect usage of parameter\n");
1521                 Usage();
1522             }
1523         }
1524 #endif
1525 #ifdef HLCSG_NULLIFY_INVISIBLE
1526 		else if (!strcasecmp(argv[i], "-nullfile"))
1527 		{
1528             if (i < argc)
1529             {
1530                 g_nullfile = argv[++i];
1531             }
1532             else
1533             {
1534             	Log("Error: -nullfile: expected path to null ent file following parameter\n");
1535                 Usage();
1536             }
1537 		}
1538 #endif
1539 
1540 #ifdef HLCSG_AUTOWAD // AJM
1541         else if (!strcasecmp(argv[i], "-wadautodetect"))
1542         {
1543             g_bWadAutoDetect = true;
1544         }
1545 #endif
1546 
1547 #ifdef ZHLT_DETAIL // AJM
1548         else if (!strcasecmp(argv[i], "-nodetail"))
1549         {
1550             g_bDetailBrushes = false;
1551         }
1552 #endif
1553 
1554 #ifdef ZHLT_PROGRESSFILE // AJM
1555         else if (!strcasecmp(argv[i], "-progressfile"))
1556         {
1557             if (i < argc)
1558             {
1559                 g_progressfile = argv[++i];
1560             }
1561             else
1562             {
1563             	Log("Error: -progressfile: expected path to progress file following parameter\n");
1564                 Usage();
1565             }
1566         }
1567 #endif
1568 
1569         else if (!strcasecmp(argv[i], "-nowadtextures"))
1570         {
1571             g_wadtextures = false;
1572         }
1573         else if (!strcasecmp(argv[i], "-wadinclude"))
1574         {
1575             if (i < argc)
1576             {
1577                 g_WadInclude.push_back(argv[++i]);
1578             }
1579             else
1580             {
1581                 Usage();
1582             }
1583         }
1584         else if (!strcasecmp(argv[i], "-texdata"))
1585         {
1586             if (i < argc)
1587             {
1588                 int             x = atoi(argv[++i]) * 1024;
1589 
1590                 if (x > g_max_map_miptex)
1591                 {
1592                     g_max_map_miptex = x;
1593                 }
1594             }
1595             else
1596             {
1597                 Usage();
1598             }
1599         }
1600         else if (!strcasecmp(argv[i], "-lightdata"))
1601         {
1602             if (i < argc)
1603             {
1604                 int             x = atoi(argv[++i]) * 1024;
1605 
1606                 if (x > g_max_map_lightdata)
1607                 {
1608                     g_max_map_lightdata = x;
1609                 }
1610             }
1611             else
1612             {
1613                 Usage();
1614             }
1615         }
1616         else if (!strcasecmp(argv[i], "-brushunion"))
1617         {
1618             if (i < argc)
1619             {
1620                 g_BrushUnionThreshold = (float)atof(argv[++i]);
1621             }
1622             else
1623             {
1624                 Usage();
1625             }
1626         }
1627         else if (!strcasecmp(argv[i], "-tiny"))
1628         {
1629             if (i < argc)
1630             {
1631                 g_tiny_threshold = (float)atof(argv[++i]);
1632             }
1633             else
1634             {
1635                 Usage();
1636             }
1637         }
1638         else if (!strcasecmp(argv[i], "-hullfile"))
1639         {
1640             if (i < argc)
1641             {
1642                 g_hullfile = argv[++i];
1643             }
1644             else
1645             {
1646                 Usage();
1647             }
1648         }
1649         else if (argv[i][0] == '-')
1650         {
1651             Log("Unknown option \"%s\"\n", argv[i]);
1652             Usage();
1653         }
1654         else if (!mapname_from_arg)
1655         {
1656             mapname_from_arg = argv[i];
1657         }
1658         else
1659         {
1660             Log("Unknown option \"%s\"\n", argv[i]);
1661             Usage();
1662         }
1663     }
1664 
1665     // no mapfile?
1666     if (!mapname_from_arg)
1667     {
1668         // what a shame.
1669         Log("No mapfile specified\n");
1670         Usage();
1671     }
1672 
1673     // handle mapname
1674     safe_strncpy(g_Mapname, mapname_from_arg, _MAX_PATH);
1675     FlipSlashes(g_Mapname);
1676     StripExtension(g_Mapname);
1677 
1678     // onlyents
1679     if (!g_onlyents)
1680         ResetTmpFiles();
1681 
1682     // other stuff
1683     ResetErrorLog();
1684     ResetLog();
1685     OpenLog(g_clientid);
1686     atexit(CloseLog);
1687     LogStart(argc, argv);
1688     atexit(CSGCleanup); // AJM
1689     dtexdata_init();
1690     atexit(dtexdata_free);
1691 
1692     // START CSG
1693     // AJM: re-arranged some stuff up here so that the mapfile is loaded
1694     //  before settings are finalised and printed out, so that the info_compile_parameters
1695     //  entity can be dealt with effectively
1696     start = I_FloatTime();
1697 
1698     LoadHullfile(g_hullfile);               // if the user specified a hull file, load it now
1699 #ifdef HLCSG_NULLIFY_INVISIBLE
1700 	if(g_bUseNullTex)
1701 	{ properties_initialize(g_nullfile); }
1702 #endif
1703     safe_strncpy(name, mapname_from_arg, _MAX_PATH); // make a copy of the nap name
1704     DefaultExtension(name, ".map");                  // might be .reg
1705 
1706     LoadMapFile(name);
1707     ThreadSetDefault();
1708     ThreadSetPriority(g_threadpriority);
1709     Settings();
1710 
1711 
1712 #ifdef HLCSG_WADCFG // AJM
1713     // figure out what to do with the texture settings
1714     if (wadconfigname[0])           // custom wad configuations will take precedence
1715     {
1716         LoadWadConfigFile();
1717         ProcessWadConfiguration();
1718     }
1719     else
1720     {
1721         Log("Using mapfile wad configuration\n");
1722     }
1723     if (!g_bWadConfigsLoaded)  // dont try and override wad.cfg
1724 #endif
1725     {
1726         GetUsedWads();
1727     }
1728 
1729 #ifdef HLCSG_AUTOWAD
1730     if (g_bWadAutoDetect)
1731     {
1732         Log("Wadfiles not in use by the map will be excluded\n");
1733     }
1734 #endif
1735 
1736     DumpWadinclude();
1737     Log("\n");
1738 
1739     // if onlyents, just grab the entites and resave
1740     if (g_onlyents)
1741     {
1742         char            out[_MAX_PATH];
1743 
1744         safe_snprintf(out, _MAX_PATH, "%s.bsp", g_Mapname);
1745         LoadBSPFile(out);
1746         LoadWadincludeFile(g_Mapname);
1747 
1748         HandleWadinclude();
1749 
1750         // Write it all back out again.
1751         if (g_chart)
1752         {
1753             PrintBSPFileSizes();
1754         }
1755         WriteBSP(g_Mapname);
1756 
1757         end = I_FloatTime();
1758         LogTimeElapsed(end - start);
1759         return 0;
1760     }
1761     else
1762     {
1763         SaveWadincludeFile(g_Mapname);
1764     }
1765 
1766 #ifdef HLCSG_CLIPECONOMY // AJM
1767     CheckForNoClip();
1768 #endif
1769 
1770     // createbrush
1771     NamedRunThreadsOnIndividual(g_nummapbrushes, g_estimate, CreateBrush);
1772     CheckFatal();
1773 
1774 #ifdef HLCSG_PRECISIONCLIP // KGP - drop TEX_BEVEL flag
1775 	FixBevelTextures();
1776 #endif
1777 
1778     // boundworld
1779     BoundWorld();
1780 
1781     Verbose("%5i map planes\n", g_nummapplanes);
1782 
1783     // Set model centers
1784     NamedRunThreadsOnIndividual(g_numentities, g_estimate, SetModelCenters);
1785 
1786     // Calc brush unions
1787     if ((g_BrushUnionThreshold > 0.0) && (g_BrushUnionThreshold <= 100.0))
1788     {
1789         NamedRunThreadsOnIndividual(g_nummapbrushes, g_estimate, CalculateBrushUnions);
1790     }
1791 
1792     // open hull files
1793     for (i = 0; i < NUM_HULLS; i++)
1794     {
1795         char            name[_MAX_PATH];
1796 
1797         safe_snprintf(name, _MAX_PATH, "%s.p%i", g_Mapname, i);
1798 
1799         out[i] = fopen(name, "w");
1800 
1801         if (!out[i])
1802             Error("Couldn't open %s", name);
1803     }
1804 
1805     ProcessModels();
1806 
1807     Verbose("%5i csg faces\n", c_csgfaces);
1808     Verbose("%5i used faces\n", c_outfaces);
1809     Verbose("%5i tiny faces\n", c_tiny);
1810     Verbose("%5i tiny clips\n", c_tiny_clip);
1811 
1812     // close hull files
1813     for (i = 0; i < NUM_HULLS; i++)
1814         fclose(out[i]);
1815 
1816     EmitPlanes();
1817 
1818     if (g_chart)
1819         PrintBSPFileSizes();
1820 
1821     WriteBSP(g_Mapname);
1822 
1823     // AJM: debug
1824 #if 0
1825     Log("\n---------------------------------------\n"
1826         "Map Plane Usage:\n"
1827         "  #  normal             origin             dist   type\n"
1828         "    (   x,    y,    z) (   x,    y,    z) (     )\n"
1829         );
1830     for (i = 0; i < g_nummapplanes; i++)
1831     {
1832         plane_t* p = &g_mapplanes[i];
1833 
1834         Log(
1835         "%3i (%4.0f, %4.0f, %4.0f) (%4.0f, %4.0f, %4.0f) (%5.0f) %i\n",
1836         i,
1837         p->normal[1], p->normal[2], p->normal[3],
1838         p->origin[1], p->origin[2], p->origin[3],
1839         p->dist,
1840         p->type
1841         );
1842     }
1843     Log("---------------------------------------\n\n");
1844 #endif
1845 
1846     // elapsed time
1847     end = I_FloatTime();
1848     LogTimeElapsed(end - start);
1849 
1850     return 0;
1851 }
1852