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