1 #pragma warning(disable: 4018) // '<' : signed/unsigned mismatch
2 
3 #include "csg.h"
4 
5 int             g_nummapbrushes;
6 brush_t         g_mapbrushes[MAX_MAP_BRUSHES];
7 
8 int             g_numbrushsides;
9 side_t          g_brushsides[MAX_MAP_SIDES];
10 
11 int             g_nMapFileVersion;
12 
13 static const vec3_t   s_baseaxis[18] = {
14     {0, 0, 1}, {1, 0, 0}, {0, -1, 0},                      // floor
15     {0, 0, -1}, {1, 0, 0}, {0, -1, 0},                     // ceiling
16     {1, 0, 0}, {0, 1, 0}, {0, 0, -1},                      // west wall
17     {-1, 0, 0}, {0, 1, 0}, {0, 0, -1},                     // east wall
18     {0, 1, 0}, {1, 0, 0}, {0, 0, -1},                      // south wall
19     {0, -1, 0}, {1, 0, 0}, {0, 0, -1},                     // north wall
20 };
21 
22 // =====================================================================================
23 //  TextureAxisFromPlane
24 // =====================================================================================
TextureAxisFromPlane(const plane_t * const pln,vec3_t xv,vec3_t yv)25 void            TextureAxisFromPlane(const plane_t* const pln, vec3_t xv, vec3_t yv)
26 {
27     int             bestaxis;
28     vec_t           dot, best;
29     int             i;
30 
31     best = 0;
32     bestaxis = 0;
33 
34     for (i = 0; i < 6; i++)
35     {
36         dot = DotProduct(pln->normal, s_baseaxis[i * 3]);
37         if (dot > best)
38         {
39             best = dot;
40             bestaxis = i;
41         }
42     }
43 
44     VectorCopy(s_baseaxis[bestaxis * 3 + 1], xv);
45     VectorCopy(s_baseaxis[bestaxis * 3 + 2], yv);
46 }
47 
48 #define ScaleCorrection	(1.0/128.0)
49 
50 // =====================================================================================
51 //  CopySKYtoCLIP
52 //      clips a particluar sky brush
53 // =====================================================================================
CopySKYtoCLIP(const brush_t * const b)54 static void     CopySKYtoCLIP(const brush_t* const b)
55 {
56     int             i;
57     entity_t*       mapent;
58     brush_t*        newbrush;
59 
60     if (b->contents != CONTENTS_SKY)
61 		Error("[MOD] CopySKYtoCLIP: Got a NON-SKY for passed brush! (%s)",b->contents );
62 
63     hlassert(b->contents == CONTENTS_SKY);                 // Only SKY brushes should be passed down to this function(sanity check)
64     hlassert(b->entitynum == 0);                           // SKY must be in worldspawn entity
65 
66     mapent = &g_entities[b->entitynum];
67     mapent->numbrushes++;
68 
69     newbrush = &g_mapbrushes[g_nummapbrushes];
70     newbrush->entitynum = b->entitynum;
71     newbrush->brushnum = g_nummapbrushes - mapent->firstbrush;
72     newbrush->firstside = g_numbrushsides;
73     newbrush->numsides = b->numsides;
74     newbrush->contents = CONTENTS_CLIP;
75     newbrush->noclip = 0;
76 
77     for (i = 0; i < b->numsides; i++)
78     {
79         int             j;
80 
81         side_t*         side = &g_brushsides[g_numbrushsides];
82 
83         *side = g_brushsides[b->firstside + i];
84         safe_strncpy(side->td.name, "CLIP", sizeof(side->td.name));
85 
86         for (j = 0; j < NUM_HULLS; j++)
87         {
88             newbrush->hulls[j].faces = NULL;
89             newbrush->hulls[j].bounds = b->hulls[j].bounds;
90         }
91 
92         g_numbrushsides++;
93         hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
94     }
95 
96     g_nummapbrushes++;
97     hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
98 }
99 
100 // =====================================================================================
101 //  HandleSKYCLIP
102 //      clips the whole sky, unconditional of g_skyclip
103 // =====================================================================================
HandleSKYCLIP()104 static void     HandleSKYCLIP()
105 {
106     int             i;
107     int             last;
108     entity_t*       e = &g_entities[0];
109 
110     for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++)
111     {
112         if (g_mapbrushes[i].contents == CONTENTS_SKY)
113         {
114             CopySKYtoCLIP(&g_mapbrushes[i]);
115         }
116     }
117 }
118 
119 // =====================================================================================
120 //  CheckForInvisible
121 //      see if a brush is part of an invisible entity (KGP)
122 // =====================================================================================
123 #ifdef HLCSG_NULLIFY_INVISIBLE
CheckForInvisible(entity_t * mapent)124 static bool CheckForInvisible(entity_t* mapent)
125 {
126 	using namespace std;
127 
128 	string keyval(ValueForKey(mapent,"classname"));
129 	if(g_invisible_items.count(keyval))
130 	{ return true; }
131 
132 	keyval.assign(ValueForKey(mapent,"targetname"));
133 	if(g_invisible_items.count(keyval))
134 	{ return true; }
135 
136 	keyval.assign(ValueForKey(mapent,"zhlt_invisible"));
137 	if(!keyval.empty() && strcmp(keyval.c_str(),"0"))
138 	{ return true; }
139 
140 	return false;
141 }
142 #endif
143 // =====================================================================================
144 //  ParseBrush
145 //      parse a brush from script
146 // =====================================================================================
ParseBrush(entity_t * mapent)147 static contents_t ParseBrush(entity_t* mapent)
148 {
149     brush_t*        b;
150     int             i, j;
151     side_t*         side;
152     contents_t      contents;
153     bool            ok;
154 #ifdef HLCSG_NULLIFY_INVISIBLE // KGP
155 	bool nullify = CheckForInvisible(mapent);
156 #endif
157     hlassume(g_nummapbrushes < MAX_MAP_BRUSHES, assume_MAX_MAP_BRUSHES);
158 
159     b = &g_mapbrushes[g_nummapbrushes];
160     g_nummapbrushes++;
161     b->firstside = g_numbrushsides;
162     b->entitynum = g_numentities - 1;
163     b->brushnum = g_nummapbrushes - mapent->firstbrush - 1;
164 
165 #ifdef HLCSG_CLIPECONOMY // AJM
166     b->noclip = 0;
167 #endif
168 
169     mapent->numbrushes++;
170 
171 	ok = GetToken(true);
172     while (ok)
173     {
174         g_TXcommand = 0;
175         if (!strcmp(g_token, "}"))
176         {
177             break;
178         }
179 
180         hlassume(g_numbrushsides < MAX_MAP_SIDES, assume_MAX_MAP_SIDES);
181         side = &g_brushsides[g_numbrushsides];
182         g_numbrushsides++;
183 
184         b->numsides++;
185 
186         // read the three point plane definition
187         for (i = 0; i < 3; i++)
188         {
189             if (i != 0)
190             {
191                 GetToken(true);
192             }
193             if (strcmp(g_token, "("))
194             {
195                 Error("Parsing Entity %i, Brush %i, Side %i : Expecting '(' got '%s'",
196                       b->entitynum, b->brushnum, b->numsides, g_token);
197             }
198 
199             for (j = 0; j < 3; j++)
200             {
201                 GetToken(false);
202                 side->planepts[i][j] = atof(g_token);
203             }
204 
205             GetToken(false);
206             if (strcmp(g_token, ")"))
207             {
208                 Error("Parsing	Entity %i, Brush %i, Side %i : Expecting ')' got '%s'",
209                       b->entitynum, b->brushnum, b->numsides, g_token);
210             }
211         }
212 
213         // read the     texturedef
214         GetToken(false);
215         _strupr(g_token);
216 #ifdef HLCSG_NULLIFY_INVISIBLE
217 		if(nullify && strncmp(g_token,"BEVEL",5) && strncmp(g_token,"ORIGIN",6))
218 		{ safe_strncpy(g_token,"NULL",sizeof(g_token)); }
219 #endif
220         safe_strncpy(side->td.name, g_token, sizeof(side->td.name));
221 
222         if (g_nMapFileVersion < 220)                       // Worldcraft 2.1-, Radiant
223         {
224             GetToken(false);
225             side->td.vects.valve.shift[0] = atof(g_token);
226             GetToken(false);
227             side->td.vects.valve.shift[1] = atof(g_token);
228             GetToken(false);
229             side->td.vects.valve.rotate = atof(g_token);
230             GetToken(false);
231             side->td.vects.valve.scale[0] = atof(g_token);
232             GetToken(false);
233             side->td.vects.valve.scale[1] = atof(g_token);
234         }
235         else                                               // Worldcraft 2.2+
236         {
237             // texture U axis
238             GetToken(false);
239             if (strcmp(g_token, "["))
240             {
241                 hlassume(false, assume_MISSING_BRACKET_IN_TEXTUREDEF);
242             }
243 
244             GetToken(false);
245             side->td.vects.valve.UAxis[0] = atof(g_token);
246             GetToken(false);
247             side->td.vects.valve.UAxis[1] = atof(g_token);
248             GetToken(false);
249             side->td.vects.valve.UAxis[2] = atof(g_token);
250             GetToken(false);
251             side->td.vects.valve.shift[0] = atof(g_token);
252 
253             GetToken(false);
254             if (strcmp(g_token, "]"))
255             {
256                 Error("missing ']' in texturedef (U)");
257             }
258 
259             // texture V axis
260             GetToken(false);
261             if (strcmp(g_token, "["))
262             {
263                 Error("missing '[' in texturedef (V)");
264             }
265 
266             GetToken(false);
267             side->td.vects.valve.VAxis[0] = atof(g_token);
268             GetToken(false);
269             side->td.vects.valve.VAxis[1] = atof(g_token);
270             GetToken(false);
271             side->td.vects.valve.VAxis[2] = atof(g_token);
272             GetToken(false);
273             side->td.vects.valve.shift[1] = atof(g_token);
274 
275             GetToken(false);
276             if (strcmp(g_token, "]"))
277             {
278                 Error("missing ']' in texturedef (V)");
279             }
280 
281             // Texture rotation is implicit in U/V axes.
282             GetToken(false);
283             side->td.vects.valve.rotate = 0;
284 
285             // texure scale
286             GetToken(false);
287             side->td.vects.valve.scale[0] = atof(g_token);
288             GetToken(false);
289             side->td.vects.valve.scale[1] = atof(g_token);
290         }
291 
292         ok = GetToken(true);                               // Done with line, this reads the first item from the next line
293 
294         if ((g_TXcommand == '1' || g_TXcommand == '2'))
295         {
296             // We are QuArK mode and need to translate some numbers to align textures its way
297             // from QuArK, the texture vectors are given directly from the three points
298             vec3_t          TexPt[2];
299             int             k;
300             float           dot22, dot23, dot33, mdet, aa, bb, dd;
301 
302             k = g_TXcommand - '0';
303             for (j = 0; j < 3; j++)
304             {
305                 TexPt[1][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection;
306             }
307             k = 3 - k;
308             for (j = 0; j < 3; j++)
309             {
310                 TexPt[0][j] = (side->planepts[k][j] - side->planepts[0][j]) * ScaleCorrection;
311             }
312 
313             dot22 = DotProduct(TexPt[0], TexPt[0]);
314             dot23 = DotProduct(TexPt[0], TexPt[1]);
315             dot33 = DotProduct(TexPt[1], TexPt[1]);
316             mdet = dot22 * dot33 - dot23 * dot23;
317             if (mdet < 1E-6 && mdet > -1E-6)
318             {
319                 aa = bb = dd = 0;
320                 Warning
321                     ("Degenerate QuArK-style brush texture : Entity %i, Brush %i @ (%f,%f,%f) (%f,%f,%f)	(%f,%f,%f)",
322                      b->entitynum, b->brushnum, side->planepts[0][0], side->planepts[0][1], side->planepts[0][2],
323                      side->planepts[1][0], side->planepts[1][1], side->planepts[1][2], side->planepts[2][0],
324                      side->planepts[2][1], side->planepts[2][2]);
325             }
326             else
327             {
328                 mdet = 1.0 / mdet;
329                 aa = dot33 * mdet;
330                 bb = -dot23 * mdet;
331                 //cc = -dot23*mdet;             // cc = bb
332                 dd = dot22 * mdet;
333             }
334 
335             for (j = 0; j < 3; j++)
336             {
337                 side->td.vects.quark.vects[0][j] = aa * TexPt[0][j] + bb * TexPt[1][j];
338                 side->td.vects.quark.vects[1][j] = -( /*cc */ bb * TexPt[0][j] + dd * TexPt[1][j]);
339             }
340 
341             side->td.vects.quark.vects[0][3] = -DotProduct(side->td.vects.quark.vects[0], side->planepts[0]);
342             side->td.vects.quark.vects[1][3] = -DotProduct(side->td.vects.quark.vects[1], side->planepts[0]);
343         }
344 
345         side->td.txcommand = g_TXcommand;                  // Quark stuff, but needs setting always
346     };
347 
348     b->contents = contents = CheckBrushContents(b);
349 
350     //
351     // origin brushes are removed, but they set
352     // the rotation origin for the rest of the brushes
353     // in the entity
354     //
355 
356     if (contents == CONTENTS_ORIGIN)
357     {
358         char            string[MAXTOKEN];
359         vec3_t          origin;
360 
361         b->contents = CONTENTS_SOLID;
362         CreateBrush(mapent->firstbrush + b->brushnum);     // to get sizes
363         b->contents = contents;
364 
365         for (i = 0; i < NUM_HULLS; i++)
366         {
367             b->hulls[i].faces = NULL;
368         }
369 
370         if (b->entitynum != 0)  // Ignore for WORLD (code elsewhere enforces no ORIGIN in world message)
371         {
372             VectorAdd(b->hulls[0].bounds.m_Mins, b->hulls[0].bounds.m_Maxs, origin);
373             VectorScale(origin, 0.5, origin);
374 
375             safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
376             SetKeyValue(&g_entities[b->entitynum], "origin", string);
377         }
378     }
379 
380     return contents;
381 }
382 
383 
384 // =====================================================================================
385 //  ParseMapEntity
386 //      parse an entity from script
387 // =====================================================================================
ParseMapEntity()388 bool            ParseMapEntity()
389 {
390     bool            all_clip = true;
391     int             this_entity;
392     entity_t*       mapent;
393     epair_t*        e;
394 
395     if (!GetToken(true))
396     {
397         return false;
398     }
399 
400     this_entity = g_numentities;
401 
402     if (strcmp(g_token, "{"))
403     {
404         Error("Parsing Entity %i, expected '{' got '%s'", this_entity, g_token);
405     }
406 
407     hlassume(g_numentities < MAX_MAP_ENTITIES, assume_MAX_MAP_ENTITIES);
408     g_numentities++;
409 
410     mapent = &g_entities[this_entity];
411     mapent->firstbrush = g_nummapbrushes;
412     mapent->numbrushes = 0;
413 
414     while (1)
415     {
416         if (!GetToken(true))
417             Error("ParseEntity: EOF without closing brace");
418 
419         if (!strcmp(g_token, "}"))  // end of our context
420             break;
421 
422         if (!strcmp(g_token, "{"))  // must be a brush
423         {
424             contents_t contents = ParseBrush(mapent);
425 
426             if ((contents != CONTENTS_CLIP) && (contents != CONTENTS_ORIGIN))
427                 all_clip = false;
428         }
429         else                        // else assume an epair
430         {
431             e = ParseEpair();
432 
433             if (!strcmp(e->key, "mapversion"))
434             {
435                 g_nMapFileVersion = atoi(e->value);
436             }
437 
438             e->next = mapent->epairs;
439             mapent->epairs = e;
440         }
441     }
442 
443 
444     if (mapent->numbrushes && all_clip)
445         Fatal(assume_NO_VISIBILE_BRUSHES, "Entity %i has no visible brushes\n", this_entity);
446 
447     CheckFatal();
448 
449 
450 #ifdef ZHLT_DETAIL // AJM
451     if (!strcmp(ValueForKey(mapent, "classname"), "info_detail") && g_bDetailBrushes && this_entity != 0)
452     {
453         // mark all of the brushes in this entity as contents_detail
454         for (int i = mapent->firstbrush; i < mapent->firstbrush + mapent->numbrushes; i++)
455         {
456             g_mapbrushes[i].contents = CONTENTS_DETAIL;
457         }
458 
459         // move these brushes to worldspawn
460         {
461             brush_t*        temp;
462             int             newbrushes;
463             int             worldbrushes;
464             int             i;
465 
466             newbrushes = mapent->numbrushes;
467             worldbrushes = g_entities[0].numbrushes;
468 
469             temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t));
470             memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t));
471 
472             for (i = 0; i < newbrushes; i++)
473             {
474                 temp[i].entitynum = 0;
475             }
476 
477             // make space to move the brushes (overlapped copy)
478             memmove(g_mapbrushes + worldbrushes + newbrushes,
479                     g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes));
480 
481             // copy the new brushes down
482             memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes);
483 
484             // fix up indexes
485             g_numentities--;
486             g_entities[0].numbrushes += newbrushes;
487             for (i = 1; i < g_numentities; i++)
488             {
489                 g_entities[i].firstbrush += newbrushes;
490             }
491             memset(mapent, 0, sizeof(*mapent));
492             Free(temp);
493         }
494 
495         // delete this entity
496         g_numentities--;
497         return true;
498     }
499 #endif
500 
501 
502 #ifdef ZHLT_INFO_COMPILE_PARAMETERS // AJM
503     if (!strcmp(ValueForKey(mapent, "classname"), "info_compile_parameters"))
504     {
505         GetParamsFromEnt(mapent);
506     }
507 #endif
508 
509     // if its the worldspawn entity and we need to skyclip, then do it
510     if ((this_entity == 0) && g_skyclip)                  // first entitiy
511     {
512         HandleSKYCLIP();
513     }
514 
515     // if the given entity only has one brush and its an origin brush
516     if ((mapent->numbrushes == 1) && (g_mapbrushes[mapent->firstbrush].contents == CONTENTS_ORIGIN))
517     {
518         brushhull_t*    hull = g_mapbrushes[mapent->firstbrush].hulls;
519 
520         Error("Entity %i, contains ONLY an origin brush near (%.0f,%.0f,%.0f)\n",
521               this_entity, hull->bounds.m_Mins[0], hull->bounds.m_Mins[1], hull->bounds.m_Mins[2]);
522     }
523 
524     GetVectorForKey(mapent, "origin", mapent->origin);
525 
526     // group entities are just for editor convenience
527     // toss all brushes into the world entity
528     if (!g_onlyents && !strcmp("func_group", ValueForKey(mapent, "classname")))
529     {
530         // this is pretty gross, because the brushes are expected to be
531         // in linear order for each entity
532         brush_t*        temp;
533         int             newbrushes;
534         int             worldbrushes;
535         int             i;
536 
537         newbrushes = mapent->numbrushes;
538         worldbrushes = g_entities[0].numbrushes;
539 
540         temp = (brush_t*)Alloc(newbrushes * sizeof(brush_t));
541         memcpy(temp, g_mapbrushes + mapent->firstbrush, newbrushes * sizeof(brush_t));
542 
543         for (i = 0; i < newbrushes; i++)
544         {
545             temp[i].entitynum = 0;
546         }
547 
548         // make space to move the brushes (overlapped copy)
549         memmove(g_mapbrushes + worldbrushes + newbrushes,
550                 g_mapbrushes + worldbrushes, sizeof(brush_t) * (g_nummapbrushes - worldbrushes - newbrushes));
551 
552         // copy the new brushes down
553         memcpy(g_mapbrushes + worldbrushes, temp, sizeof(brush_t) * newbrushes);
554 
555         // fix up indexes
556         g_numentities--;
557         g_entities[0].numbrushes += newbrushes;
558         for (i = 1; i < g_numentities; i++)
559         {
560             g_entities[i].firstbrush += newbrushes;
561         }
562         memset(mapent, 0, sizeof(*mapent));
563         Free(temp);
564     }
565 
566     return true;
567 }
568 
569 // =====================================================================================
570 //  CountEngineEntities
571 // =====================================================================================
CountEngineEntities()572 unsigned int    CountEngineEntities()
573 {
574     unsigned int x;
575     unsigned num_engine_entities = 0;
576     entity_t*       mapent = g_entities;
577 
578     // for each entity in the map
579     for (x=0; x<g_numentities; x++, mapent++)
580     {
581         const char* classname = ValueForKey(mapent, "classname");
582 
583         // if its a light_spot or light_env, dont include it as an engine entity!
584         if (classname)
585         {
586             if (   !strncasecmp(classname, "light", 5)
587                 || !strncasecmp(classname, "light_spot", 10)
588                 || !strncasecmp(classname, "light_environment", 17)
589                )
590             {
591                 const char* style = ValueForKey(mapent, "style");
592                 const char* targetname = ValueForKey(mapent, "targetname");
593 
594                 // lightspots and lightenviroments dont have a targetname or style
595                 if (!strlen(targetname) && !atoi(style))
596                 {
597                     continue;
598                 }
599             }
600         }
601 
602         num_engine_entities++;
603     }
604 
605     return num_engine_entities;
606 }
607 
608 // =====================================================================================
609 //  LoadMapFile
610 //      wrapper for LoadScriptFile
611 //      parse in script entities
612 // =====================================================================================
613 const char*     ContentsToString(const contents_t type);
614 
LoadMapFile(const char * const filename)615 void            LoadMapFile(const char* const filename)
616 {
617     unsigned num_engine_entities;
618 
619     LoadScriptFile(filename);
620 
621     g_numentities = 0;
622 
623     while (ParseMapEntity())
624     {
625     }
626 
627     // AJM debug
628     /*
629     for (int i = 0; i < g_numentities; i++)
630     {
631         Log("entity: %i - %i brushes - %s\n", i, g_entities[i].numbrushes, ValueForKey(&g_entities[i], "classname"));
632     }
633     Log("total entities: %i\ntotal brushes: %i\n\n", g_numentities, g_nummapbrushes);
634 
635     for (i = g_entities[0].firstbrush; i < g_entities[0].firstbrush + g_entities[0].numbrushes; i++)
636     {
637         Log("worldspawn brush %i: contents %s\n", i, ContentsToString((contents_t)g_mapbrushes[i].contents));
638     }
639     */
640 
641     num_engine_entities = CountEngineEntities();
642 
643     hlassume(num_engine_entities < MAX_ENGINE_ENTITIES, assume_MAX_ENGINE_ENTITIES);
644 
645     CheckFatal();
646 
647     Verbose("Load map:%s\n", filename);
648     Verbose("%5i brushes\n", g_nummapbrushes);
649     Verbose("%5i map entities \n", g_numentities - num_engine_entities);
650     Verbose("%5i engine entities\n", num_engine_entities);
651 
652     // AJM: added in
653 #ifdef HLCSG_AUTOWAD
654     GetUsedTextures();
655 #endif
656 }
657