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