00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "map.h"
00027 #include "bsp.h"
00028 #include "check/check.h"
00029 #include "check/checkentities.h"
00030 #include "common/aselib.h"
00031
00032 mapbrush_t mapbrushes[MAX_MAP_BRUSHES];
00033 int nummapbrushes;
00034
00035 side_t brushsides[MAX_MAP_SIDES];
00036 int nummapbrushsides;
00037
00038 brush_texture_t side_brushtextures[MAX_MAP_SIDES];
00039
00041 plane_t mapplanes[MAX_MAP_PLANES];
00042 int nummapplanes;
00043
00044 #define PLANE_HASHES 1024
00045 static plane_t *planehash[PLANE_HASHES];
00046
00047 static vec3_t map_mins, map_maxs;
00048 static int c_boxbevels = 0;
00049 static int c_edgebevels = 0;
00050
00051
00052
00053
00054
00055
00056
00061 static inline int GetPlaneHashValueForDistance (const vec_t distance)
00062 {
00063 int hash = (int)fabs(distance) / 8;
00064 hash &= (PLANE_HASHES - 1);
00065 return hash;
00066 }
00067
00071 static inline int PlaneTypeForNormal (const vec3_t normal)
00072 {
00073 vec_t ax, ay, az;
00074
00075
00076 if (normal[0] == 1.0 || normal[0] == -1.0)
00077 return PLANE_X;
00078 if (normal[1] == 1.0 || normal[1] == -1.0)
00079 return PLANE_Y;
00080 if (normal[2] == 1.0 || normal[2] == -1.0)
00081 return PLANE_Z;
00082
00083 ax = fabs(normal[0]);
00084 ay = fabs(normal[1]);
00085 az = fabs(normal[2]);
00086
00087 if (ax >= ay && ax >= az)
00088 return PLANE_ANYX;
00089 if (ay >= ax && ay >= az)
00090 return PLANE_ANYY;
00091 return PLANE_ANYZ;
00092 }
00093
00101 static inline qboolean PlaneEqual (const plane_t *p, const vec3_t normal, const vec_t dist)
00102 {
00103 if (fabs(p->normal[0] - normal[0]) < NORMAL_EPSILON
00104 && fabs(p->normal[1] - normal[1]) < NORMAL_EPSILON
00105 && fabs(p->normal[2] - normal[2]) < NORMAL_EPSILON
00106 && fabs(p->dist - dist) < MAP_DIST_EPSILON)
00107 return qtrue;
00108 return qfalse;
00109 }
00110
00111 static inline void AddPlaneToHash (plane_t *p)
00112 {
00113 const int hash = GetPlaneHashValueForDistance(p->dist);
00114 p->hash_chain = planehash[hash];
00115 planehash[hash] = p;
00116 }
00117
00118 static inline int CreateNewFloatPlane (vec3_t normal, vec_t dist)
00119 {
00120 plane_t *p;
00121
00122 if (VectorLength(normal) < 0.5)
00123 Sys_Error("FloatPlane: bad normal (%.3f:%.3f:%.3f)", normal[0], normal[1], normal[2]);
00124
00125 if (nummapplanes + 2 > MAX_MAP_PLANES)
00126 Sys_Error("MAX_MAP_PLANES (%i)", nummapplanes + 2);
00127
00128 p = &mapplanes[nummapplanes];
00129 VectorCopy(normal, p->normal);
00130 p->dist = dist;
00131 p->type = (p + 1)->type = PlaneTypeForNormal(p->normal);
00132
00133 VectorSubtract(vec3_origin, normal, (p + 1)->normal);
00134 (p + 1)->dist = -dist;
00135
00136 nummapplanes += 2;
00137
00138
00139 if (AXIAL(p)) {
00140 if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) {
00141
00142 const plane_t temp = *p;
00143 *p = *(p + 1);
00144 *(p + 1) = temp;
00145
00146 AddPlaneToHash(p);
00147 AddPlaneToHash(p + 1);
00148 return nummapplanes - 1;
00149 }
00150 }
00151
00152 AddPlaneToHash(p);
00153 AddPlaneToHash(p + 1);
00154 return nummapplanes - 2;
00155 }
00156
00162 static inline qboolean SnapVector (vec3_t normal)
00163 {
00164 int i;
00165
00166 for (i = 0; i < 3; i++) {
00167 if (fabs(normal[i] - 1) < NORMAL_EPSILON) {
00168 VectorClear(normal);
00169 normal[i] = 1;
00170 return qtrue;
00171 }
00172 if (fabs(normal[i] - -1) < NORMAL_EPSILON) {
00173 VectorClear(normal);
00174 normal[i] = -1;
00175 return qtrue;
00176 }
00177 }
00178 return qfalse;
00179 }
00180
00187 static inline void SnapPlane (vec3_t normal, vec_t *dist)
00188 {
00189 SnapVector(normal);
00190
00191 if (fabs(*dist - Q_rint(*dist)) < MAP_DIST_EPSILON)
00192 *dist = Q_rint(*dist);
00193 }
00194
00195 int FindOrCreateFloatPlane (vec3_t normal, vec_t dist)
00196 {
00197 int i;
00198 plane_t *p;
00199 int hash;
00200
00201 SnapPlane(normal, &dist);
00202 hash = GetPlaneHashValueForDistance(dist);
00203
00204
00205 for (i = -1; i <= 1; i++) {
00206 const int h = (hash + i) & (PLANE_HASHES - 1);
00207 for (p = planehash[h]; p; p = p->hash_chain) {
00208 if (PlaneEqual(p, normal, dist))
00209 return p - mapplanes;
00210 }
00211 }
00212
00213 return CreateNewFloatPlane(normal, dist);
00214 }
00215
00226 static int PlaneFromPoints (const mapbrush_t *b, const vec3_t p0, const vec3_t p1, const vec3_t p2)
00227 {
00228 vec3_t t1, t2, normal;
00229 vec_t dist;
00230
00231 VectorSubtract(p0, p1, t1);
00232 VectorSubtract(p2, p1, t2);
00233 CrossProduct(t1, t2, normal);
00234 VectorNormalize(normal);
00235
00236 dist = DotProduct(p0, normal);
00237
00238 if (!VectorNotEmpty(normal))
00239 Sys_Error("PlaneFromPoints: Bad normal (null) for brush %i", b->brushnum);
00240
00241 return FindOrCreateFloatPlane(normal, dist);
00242 }
00243
00244
00245
00246
00247
00253 static int BrushContents (mapbrush_t *b)
00254 {
00255 int contentFlags, i;
00256 const side_t *s;
00257
00258 s = &b->original_sides[0];
00259 contentFlags = s->contentFlags;
00260 for (i = 1; i < b->numsides; i++, s++) {
00261 if (s->contentFlags != contentFlags) {
00262 Verb_Printf(VERB_EXTRA, "Entity %i, Brush %i: mixed face contents (f: %i, %i)\n"
00263 , b->entitynum, b->brushnum, s->contentFlags, contentFlags);
00264 break;
00265 }
00266 }
00267
00268 return contentFlags;
00269 }
00270
00276 byte GetLevelFlagsFromBrush (const mapbrush_t *brush)
00277 {
00278 const byte levelflags = (brush->contentFlags >> 8) & 0xFF;
00279 return levelflags;
00280 }
00281
00282
00283
00288 static void AddBrushBevels (mapbrush_t *b)
00289 {
00290 int axis, dir;
00291 int i, j, l, order;
00292 vec3_t normal;
00293
00294
00295 order = 0;
00296 for (axis = 0; axis < 3; axis++) {
00297 for (dir = -1; dir <= 1; dir += 2, order++) {
00298 side_t *s;
00299
00300 for (i = 0, s = b->original_sides; i < b->numsides; i++, s++) {
00301 if (mapplanes[s->planenum].normal[axis] == dir)
00302 break;
00303 }
00304
00305 if (i == b->numsides) {
00306 float dist;
00307 if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
00308 Sys_Error("MAX_MAP_BRUSHSIDES (%i)", nummapbrushsides);
00309 nummapbrushsides++;
00310 b->numsides++;
00311 VectorClear(normal);
00312 normal[axis] = dir;
00313 if (dir == 1)
00314 dist = b->maxs[axis];
00315 else
00316 dist = -b->mins[axis];
00317 s->planenum = FindOrCreateFloatPlane(normal, dist);
00318 s->texinfo = b->original_sides[0].texinfo;
00319 s->contentFlags = b->original_sides[0].contentFlags;
00320 s->bevel = qtrue;
00321 c_boxbevels++;
00322 }
00323
00324
00325 if (i != order) {
00326 const ptrdiff_t index = b->original_sides - brushsides;
00327 side_t sidetemp = b->original_sides[order];
00328 brush_texture_t tdtemp = side_brushtextures[index + order];
00329
00330 b->original_sides[order] = b->original_sides[i];
00331 b->original_sides[i] = sidetemp;
00332
00333 side_brushtextures[index + order] = side_brushtextures[index + i];
00334 side_brushtextures[index + i] = tdtemp;
00335 }
00336 }
00337 }
00338
00339
00340 if (b->numsides == 6)
00341 return;
00342
00343
00344 for (i = 6; i < b->numsides; i++) {
00345 side_t *s = b->original_sides + i;
00346 winding_t *w = s->winding;
00347 if (!w)
00348 continue;
00349
00350 for (j = 0; j < w->numpoints; j++) {
00351 int k = (j + 1) % w->numpoints;
00352 vec3_t vec;
00353
00354 VectorSubtract(w->p[j], w->p[k], vec);
00355 if (VectorNormalize(vec) < 0.5)
00356 continue;
00357
00358 SnapVector(vec);
00359
00360 for (k = 0; k < 3; k++)
00361 if (vec[k] == -1 || vec[k] == 1
00362 || (vec[k] == 0.0f && vec[(k + 1) % 3] == 0.0f))
00363 break;
00364 if (k != 3)
00365 continue;
00366
00367
00368 for (axis = 0; axis < 3; axis++) {
00369 for (dir = -1; dir <= 1; dir += 2) {
00370
00371 vec3_t vec2 = {0, 0, 0};
00372 float dist;
00373 side_t *s2;
00374
00375 vec2[axis] = dir;
00376 CrossProduct(vec, vec2, normal);
00377 if (VectorNormalize(normal) < 0.5)
00378 continue;
00379 dist = DotProduct(w->p[j], normal);
00380
00381
00382
00383 for (k = 0; k < b->numsides; k++) {
00384 winding_t *w2;
00385 float minBack;
00386
00387
00388
00389
00390
00391 if (PlaneEqual(&mapplanes[b->original_sides[k].planenum], normal, dist))
00392 break;
00393
00394 w2 = b->original_sides[k].winding;
00395 if (!w2)
00396 continue;
00397 minBack = 0.0f;
00398 for (l = 0; l < w2->numpoints; l++) {
00399 const float d = DotProduct(w2->p[l], normal) - dist;
00400 if (d > 0.1)
00401 break;
00402 if (d < minBack)
00403 minBack = d;
00404 }
00405
00406 if (l != w2->numpoints)
00407 break;
00408
00409 if (minBack > -0.1f)
00410 break;
00411 }
00412
00413 if (k != b->numsides)
00414 continue;
00415
00416 if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
00417 Sys_Error("MAX_MAP_BRUSHSIDES (%i)", nummapbrushsides);
00418 nummapbrushsides++;
00419 s2 = &b->original_sides[b->numsides];
00420 s2->planenum = FindOrCreateFloatPlane(normal, dist);
00421 s2->texinfo = b->original_sides[0].texinfo;
00422 s2->contentFlags = b->original_sides[0].contentFlags;
00423 s2->bevel = qtrue;
00424 c_edgebevels++;
00425 b->numsides++;
00426 }
00427 }
00428 }
00429 }
00430 }
00431
00435 static qboolean MakeBrushWindings (mapbrush_t *brush)
00436 {
00437 int i, j;
00438 side_t *side;
00439
00440 ClearBounds(brush->mins, brush->maxs);
00441
00442 for (i = 0; i < brush->numsides; i++) {
00443 const plane_t *plane = &mapplanes[brush->original_sides[i].planenum];
00444 winding_t *w = BaseWindingForPlane(plane->normal, plane->dist);
00445 for (j = 0; j < brush->numsides && w; j++) {
00446 if (i == j)
00447 continue;
00448
00449 if (brush->original_sides[j].planenum == (brush->original_sides[j].planenum ^ 1))
00450 continue;
00451 if (brush->original_sides[j].bevel)
00452 continue;
00453 plane = &mapplanes[brush->original_sides[j].planenum ^ 1];
00454 ChopWindingInPlace(&w, plane->normal, plane->dist, 0);
00455 }
00456
00457 side = &brush->original_sides[i];
00458 side->winding = w;
00459 if (w) {
00460 side->visible = qtrue;
00461 for (j = 0; j < w->numpoints; j++)
00462 AddPointToBounds(w->p[j], brush->mins, brush->maxs);
00463 }
00464 }
00465
00466 for (i = 0; i < 3; i++) {
00467 if (brush->mins[0] < -MAX_WORLD_WIDTH || brush->maxs[0] > MAX_WORLD_WIDTH)
00468 Com_Printf("entity %i, brush %i: bounds out of world range (%f:%f)\n",
00469 brush->entitynum, brush->brushnum, brush->mins[0], brush->maxs[0]);
00470 if (brush->mins[0] > MAX_WORLD_WIDTH || brush->maxs[0] < -MAX_WORLD_WIDTH) {
00471 Com_Printf("entity %i, brush %i: no visible sides on brush\n", brush->entitynum, brush->brushnum);
00472 VectorClear(brush->mins);
00473 VectorClear(brush->maxs);
00474 }
00475 }
00476
00477 return qtrue;
00478 }
00479
00485 static inline void CheckFlags (side_t *side, const mapbrush_t *b)
00486 {
00487 if ((side->contentFlags & CONTENTS_ACTORCLIP) && (side->contentFlags & CONTENTS_PASSABLE))
00488 Sys_Error("Brush %i (entity %i) has invalid mix of passable and actorclip", b->brushnum, b->entitynum);
00489 if ((side->contentFlags & (CONTENTS_LIGHTCLIP | CONTENTS_ACTORCLIP | CONTENTS_WEAPONCLIP)) && (side->contentFlags & CONTENTS_SOLID))
00490 Sys_Error("Brush %i (entity %i) has invalid mix of clips and solid flags", b->brushnum, b->entitynum);
00491 }
00492
00494 static int materialsCnt = 0;
00495
00499 static inline void GenerateMaterialFile (const char *filename, int mipTexIndex, side_t *s)
00500 {
00501 qFILE f;
00502 qboolean terrainByTexture = qfalse;
00503 char fileBase[MAX_OSPATH], materialPath[MAX_OSPATH];
00504
00505 if (!config.generateMaterialFile)
00506 return;
00507
00508
00509 if (textureref[mipTexIndex].materialMarked)
00510 return;
00511
00512 assert(filename);
00513
00514 Com_StripExtension(filename, fileBase, sizeof(fileBase));
00515 Com_sprintf(materialPath, sizeof(materialPath), "materials/%s.mat", Com_SkipPath(fileBase));
00516
00517 FS_OpenFile(materialPath, &f, FILE_APPEND);
00518 if (!f.f) {
00519 Com_Printf("Could not open material file '%s' for writing\n", materialPath);
00520 config.generateMaterialFile = qfalse;
00521 return;
00522 }
00523
00524 if (strstr(textureref[mipTexIndex].name, "dirt")
00525 || strstr(textureref[mipTexIndex].name, "rock")
00526 || strstr(textureref[mipTexIndex].name, "grass")) {
00527 terrainByTexture = qtrue;
00528 }
00529
00530 if (s->contentFlags & CONTENTS_TERRAIN || terrainByTexture) {
00531 FS_Printf(&f, "{\n\tmaterial %s\n\t{\n\t\ttexture <fillme>\n\t\tterrain 0 64\n\t\tlightmap\n\t}\n}\n", textureref[mipTexIndex].name);
00532 textureref[mipTexIndex].materialMarked = qtrue;
00533 materialsCnt++;
00534 }
00535
00536
00537 if ((s->contentFlags & CONTENTS_WATER)
00538 || strstr(textureref[mipTexIndex].name, "glass")
00539 || strstr(textureref[mipTexIndex].name, "window")) {
00540 FS_Printf(&f, "{\n\tmaterial %s\n\tspecular 2.0\n\t{\n\t\tenvmap 0\n\t}\n}\n", textureref[mipTexIndex].name);
00541 textureref[mipTexIndex].materialMarked = qtrue;
00542 materialsCnt++;
00543 }
00544
00545 if (strstr(textureref[mipTexIndex].name, "wood")) {
00546 FS_Printf(&f, "{\n\tmaterial %s\n\tspecular 0.2\n}\n", textureref[mipTexIndex].name);
00547 textureref[mipTexIndex].materialMarked = qtrue;
00548 materialsCnt++;
00549 }
00550
00551 if (strstr(textureref[mipTexIndex].name, "wall")) {
00552 FS_Printf(&f, "{\n\tmaterial %s\n\tspecular 0.6\n\tbump 2.0\n}\n", textureref[mipTexIndex].name);
00553 textureref[mipTexIndex].materialMarked = qtrue;
00554 materialsCnt++;
00555 }
00556
00557 FS_CloseFile(&f);
00558 }
00559
00561 static int footstepsCnt = 0;
00562
00571 static inline void GenerateFootstepList (const char *filename, int mipTexIndex)
00572 {
00573 qFILE f;
00574 char fileBase[MAX_OSPATH];
00575
00576 if (!config.generateFootstepFile)
00577 return;
00578
00579 if (textureref[mipTexIndex].footstepMarked)
00580 return;
00581
00582 assert(filename);
00583
00584 Com_StripExtension(filename, fileBase, sizeof(fileBase));
00585
00586 FS_OpenFile(va("%s.footsteps", fileBase), &f, FILE_APPEND);
00587 if (!f.f) {
00588 Com_Printf("Could not open footstep file '%s.footsteps' for writing\n", fileBase);
00589 config.generateFootstepFile = qfalse;
00590 return;
00591 }
00592 #ifdef _WIN32
00593 FS_Printf(&f, "terrain %s {\n}\n\n", textureref[mipTexIndex].name);
00594 #else
00595 FS_Printf(&f, "%s\n", textureref[mipTexIndex].name);
00596 #endif
00597 FS_CloseFile(&f);
00598 footstepsCnt++;
00599 textureref[mipTexIndex].footstepMarked = qtrue;
00600 }
00601
00608 static void ParseBrush (entity_t *mapent, const char *filename)
00609 {
00610 mapbrush_t *b;
00611 int i, j, k, m, mt;
00612 side_t *side;
00613 int planenum;
00614 brush_texture_t td;
00615 vec3_t planepts[3];
00616 const int checkOrFix = config.performMapCheck || config.fixMap ;
00617
00618 if (nummapbrushes == MAX_MAP_BRUSHES)
00619 Sys_Error("nummapbrushes == MAX_MAP_BRUSHES (%i)", nummapbrushes);
00620
00621 b = &mapbrushes[nummapbrushes];
00622 memset(b, 0, sizeof(*b));
00623 b->original_sides = &brushsides[nummapbrushsides];
00624 b->entitynum = num_entities - 1;
00625 b->brushnum = nummapbrushes - mapent->firstbrush;
00626
00627 do {
00628 if (!GetToken(qtrue))
00629 break;
00630 if (*parsedToken == '}')
00631 break;
00632
00633 if (nummapbrushsides == MAX_MAP_BRUSHSIDES)
00634 Sys_Error("nummapbrushsides == MAX_MAP_BRUSHSIDES (%i)", nummapbrushsides);
00635 side = &brushsides[nummapbrushsides];
00636
00637
00638 for (i = 0; i < 3; i++) {
00639 if (i != 0)
00640 GetToken(qtrue);
00641 if (*parsedToken != '(')
00642 Sys_Error("parsing brush at line %i", GetScriptLine());
00643
00644 for (j = 0; j < 3; j++) {
00645 GetToken(qfalse);
00646 planepts[i][j] = atof(parsedToken);
00647 }
00648
00649 GetToken(qfalse);
00650 if (*parsedToken != ')')
00651 Sys_Error("parsing brush at line %i", GetScriptLine());
00652 }
00653
00654
00655 GetToken(qfalse);
00656 if (strlen(parsedToken) >= MAX_TEXPATH) {
00657 if (config.performMapCheck || config.fixMap)
00658 Com_Printf(" ");
00659 Com_Printf("ParseBrush: texture name too long (limit %i): %s\n", MAX_TEXPATH, parsedToken);
00660 if (config.fixMap)
00661 Sys_Error("Aborting, as -fix is active and saving might corrupt *.map by truncating texture name");
00662 }
00663 Q_strncpyz(td.name, parsedToken, sizeof(td.name));
00664
00665 GetToken(qfalse);
00666 td.shift[0] = atof(parsedToken);
00667 GetToken(qfalse);
00668 td.shift[1] = atof(parsedToken);
00669 GetToken(qfalse);
00670 td.rotate = atof(parsedToken);
00671 GetToken(qfalse);
00672 td.scale[0] = atof(parsedToken);
00673 GetToken(qfalse);
00674 td.scale[1] = atof(parsedToken);
00675
00676
00677 mt = FindMiptex(td.name);
00678 side->surfaceFlags = td.surfaceFlags = side->contentFlags = td.value = 0;
00679
00680 if (TokenAvailable()) {
00681 GetToken(qfalse);
00682 side->contentFlags = atoi(parsedToken);
00683 GetToken(qfalse);
00684 side->surfaceFlags = td.surfaceFlags = atoi(parsedToken);
00685 GetToken(qfalse);
00686 td.value = atoi(parsedToken);
00687 }
00688
00689
00690
00691 if (!checkOrFix) {
00692 SetImpliedFlags(side, &td, b);
00693
00694 if (!checkOrFix && side->contentFlags == 0)
00695 side->contentFlags = CONTENTS_SOLID;
00696 }
00697
00698
00699 if (side->surfaceFlags & (SURF_BLEND33 | SURF_BLEND66 | SURF_ALPHATEST)) {
00700 side->contentFlags |= CONTENTS_DETAIL;
00701 side->contentFlags |= CONTENTS_TRANSLUCENT;
00702 side->contentFlags |= CONTENTS_WINDOW;
00703 side->contentFlags &= ~CONTENTS_SOLID;
00704 }
00705 if (config.fulldetail)
00706 side->contentFlags &= ~CONTENTS_DETAIL;
00707 if (!checkOrFix) {
00708 if (!(side->contentFlags & ((LAST_VISIBLE_CONTENTS - 1)
00709 | CONTENTS_ACTORCLIP | CONTENTS_WEAPONCLIP | CONTENTS_LIGHTCLIP)))
00710 side->contentFlags |= CONTENTS_SOLID;
00711
00712
00713 if (side->surfaceFlags & (SURF_HINT | SURF_SKIP)) {
00714 side->contentFlags = 0;
00715 side->surfaceFlags &= ~CONTENTS_DETAIL;
00716 }
00717 }
00718
00719
00720 CheckFlags(side, b);
00721
00722
00723 if (mt > 0 && side->surfaceFlags & SURF_FOOTSTEP)
00724 GenerateFootstepList(filename, mt);
00725 GenerateMaterialFile(filename, mt, side);
00726
00727
00728 planenum = PlaneFromPoints(b, planepts[0], planepts[1], planepts[2]);
00729 if (planenum == PLANENUM_LEAF) {
00730 Com_Printf("Entity %i, Brush %i: plane with no normal at line %i\n", b->entitynum, b->brushnum, GetScriptLine());
00731 continue;
00732 }
00733
00734 for (j = 0; j < 3; j++)
00735 VectorCopy(planepts[j], mapplanes[planenum].planeVector[j]);
00736
00737
00738 for (k = 0; k < b->numsides; k++) {
00739 const side_t *s2 = b->original_sides + k;
00740 if (s2->planenum == planenum) {
00741 Com_Printf("Entity %i, Brush %i: duplicate plane at line %i\n", b->entitynum, b->brushnum, GetScriptLine());
00742 break;
00743 }
00744 if (s2->planenum == (planenum ^ 1)) {
00745 Com_Printf("Entity %i, Brush %i: mirrored plane at line %i\n", b->entitynum, b->brushnum, GetScriptLine());
00746 break;
00747 }
00748 }
00749 if (k != b->numsides)
00750 continue;
00751
00752
00753 side = b->original_sides + b->numsides;
00754 side->planenum = planenum;
00755 side->texinfo = TexinfoForBrushTexture(&mapplanes[planenum],
00756 &td, vec3_origin, side->contentFlags & CONTENTS_TERRAIN);
00757 side->brush = b;
00758
00759
00760
00761 side_brushtextures[nummapbrushsides] = td;
00762
00763 Verb_Printf(VERB_DUMP, "Brush %i Side %i (%f %f %f) (%f %f %f) (%f %f %f) texinfo:%i[%s] plane:%i\n", nummapbrushes, nummapbrushsides,
00764 planepts[0][0], planepts[0][1], planepts[0][2],
00765 planepts[1][0], planepts[1][1], planepts[1][2],
00766 planepts[2][0], planepts[2][1], planepts[2][2],
00767 side->texinfo, td.name, planenum);
00768
00769 nummapbrushsides++;
00770 b->numsides++;
00771 } while (1);
00772
00773
00774 b->contentFlags = BrushContents(b);
00775
00776
00777 for (m = 0; m < b->numsides; m++)
00778 b->contentFlags |= b->original_sides[m].contentFlags;
00779
00780
00781
00782 if (!checkOrFix)
00783 CheckPropagateParserContentFlags(b);
00784
00785
00786 if (config.nodetail && (b->contentFlags & CONTENTS_DETAIL)) {
00787 b->numsides = 0;
00788 return;
00789 }
00790
00791
00792 if (config.nowater && (b->contentFlags & CONTENTS_WATER)) {
00793 b->numsides = 0;
00794 return;
00795 }
00796
00797
00798 MakeBrushWindings(b);
00799
00800 Verb_Printf(VERB_DUMP, "Brush %i mins (%f %f %f) maxs (%f %f %f)\n", nummapbrushes,
00801 b->mins[0], b->mins[1], b->mins[2],
00802 b->maxs[0], b->maxs[1], b->maxs[2]);
00803
00804
00805
00806
00807
00808 if (!checkOrFix && b->contentFlags & CONTENTS_ORIGIN) {
00809 char string[32];
00810 vec3_t origin;
00811
00812 if (num_entities == 1) {
00813 Sys_Error("Entity %i, Brush %i: origin brushes not allowed in world"
00814 , b->entitynum, b->brushnum);
00815 return;
00816 }
00817
00818 VectorCenterFromMinsMaxs(b->mins, b->maxs, origin);
00819
00820 Com_sprintf(string, sizeof(string), "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
00821 SetKeyValue(&entities[b->entitynum], "origin", string);
00822 Verb_Printf(VERB_EXTRA, "Entity %i, Brush %i: set origin to %s\n", b->entitynum, b->brushnum, string);
00823
00824 VectorCopy(origin, entities[b->entitynum].origin);
00825
00826
00827 b->numsides = 0;
00828
00829 return;
00830 }
00831
00832 if (!checkOrFix)
00833 AddBrushBevels(b);
00834
00835 nummapbrushes++;
00836 mapent->numbrushes++;
00837 }
00838
00847 static void MoveBrushesToWorld (entity_t *mapent)
00848 {
00849 int newbrushes, worldbrushes, i;
00850 mapbrush_t *temp;
00851
00852
00853
00854
00855 newbrushes = mapent->numbrushes;
00856 worldbrushes = entities[0].numbrushes;
00857
00858 if (newbrushes == 0)
00859 Sys_Error("Empty func_group - clean your map");
00860
00861 temp = Mem_Alloc(newbrushes * sizeof(*temp));
00862 memcpy(temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof(*temp));
00863
00864
00865 memmove(mapbrushes + worldbrushes + newbrushes,
00866 mapbrushes + worldbrushes,
00867 sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes));
00868
00869
00870 memcpy(mapbrushes + worldbrushes, temp, sizeof(*temp) * newbrushes);
00871
00872
00873 entities[0].numbrushes += newbrushes;
00874 for (i = 1; i < num_entities; i++)
00875 entities[i].firstbrush += newbrushes;
00876 Mem_Free(temp);
00877
00878 mapent->numbrushes = 0;
00879 }
00880
00885 static void AdjustBrushesForOrigin (const entity_t *ent)
00886 {
00887 int i, j;
00888
00889 for (i = 0; i < ent->numbrushes; i++) {
00890 mapbrush_t *b = &mapbrushes[ent->firstbrush + i];
00891 for (j = 0; j < b->numsides; j++) {
00892 side_t *s = &b->original_sides[j];
00893 const ptrdiff_t index = s - brushsides;
00894 const vec_t newdist = mapplanes[s->planenum].dist -
00895 DotProduct(mapplanes[s->planenum].normal, ent->origin);
00896 s->surfaceFlags |= SURF_ORIGIN;
00897 side_brushtextures[index].surfaceFlags |= SURF_ORIGIN;
00898 s->planenum = FindOrCreateFloatPlane(mapplanes[s->planenum].normal, newdist);
00899 s->texinfo = TexinfoForBrushTexture(&mapplanes[s->planenum],
00900 &side_brushtextures[index], ent->origin, s->contentFlags & CONTENTS_TERRAIN);
00901 s->brush = b;
00902 }
00903
00904 MakeBrushWindings(b);
00905 }
00906 }
00907
00913 static inline qboolean IsInlineModelEntity (const char *entName)
00914 {
00915 const qboolean inlineModelEntity = (!strcmp("func_breakable", entName)
00916 || !strcmp("func_door", entName)
00917 || !strcmp("func_rotating", entName));
00918 return inlineModelEntity;
00919 }
00920
00927 entity_t *FindTargetEntity (const char *target)
00928 {
00929 int i;
00930
00931 for (i = 0; i < num_entities; i++) {
00932 const char *n = ValueForKey(&entities[i], "targetname");
00933 if (!strcmp(n, target))
00934 return &entities[i];
00935 }
00936
00937 return NULL;
00938 }
00939
00945 static qboolean ParseMapEntity (const char *filename)
00946 {
00947 entity_t *mapent;
00948 const char *entName;
00949 static int worldspawnCount = 0;
00950 int notCheckOrFix = !(config.performMapCheck || config.fixMap);
00951
00952 if (!GetToken(qtrue))
00953 return qfalse;
00954
00955 if (*parsedToken != '{')
00956 Sys_Error("ParseMapEntity: { not found");
00957
00958 if (num_entities == MAX_MAP_ENTITIES)
00959 Sys_Error("num_entities == MAX_MAP_ENTITIES (%i)", num_entities);
00960
00961 mapent = &entities[num_entities++];
00962 memset(mapent, 0, sizeof(*mapent));
00963 mapent->firstbrush = nummapbrushes;
00964 mapent->numbrushes = 0;
00965
00966 do {
00967 if (!GetToken(qtrue))
00968 Sys_Error("ParseMapEntity: EOF without closing brace");
00969 if (*parsedToken == '}')
00970 break;
00971 if (*parsedToken == '{')
00972 ParseBrush(mapent, filename);
00973 else {
00974 epair_t *e = ParseEpair();
00975 e->next = mapent->epairs;
00976 mapent->epairs = e;
00977 }
00978 } while (qtrue);
00979
00980 GetVectorForKey(mapent, "origin", mapent->origin);
00981
00982
00983
00984 entName = ValueForKey(mapent, "classname");
00985
00986
00987 if (IsInlineModelEntity(entName) && VectorNotEmpty(mapent->origin))
00988 AdjustBrushesForOrigin(mapent);
00989
00990 if (num_entities == 1 && strcmp("worldspawn", entName))
00991 Sys_Error("The first entity must be worldspawn, it is: %s", entName);
00992 if (notCheckOrFix && !strcmp("func_group", entName)) {
00993 MoveBrushesToWorld(mapent);
00994 num_entities--;
00995 } else if (IsInlineModelEntity(entName)) {
00996 if (mapent->numbrushes == 0 && notCheckOrFix) {
00997 num_entities--;
00998 Com_Printf("Warning: %s has no brushes assigned (entnum: %i)\n", entName, num_entities + 1);
00999 }
01000 } else if (!strcmp("worldspawn", entName)) {
01001 worldspawnCount++;
01002 if (worldspawnCount > 1)
01003 Com_Printf("Warning: more than one %s in one map\n", entName);
01004 }
01005 return qtrue;
01006 }
01007
01012 static inline void WriteMapEntities (qFILE *f, const epair_t *e)
01013 {
01014 if (!e)
01015 return;
01016
01017 if (e->next)
01018 WriteMapEntities(f, e->next);
01019
01020 FS_Printf(f, "\"%s\" \"%s\"\n", e->key, e->value);
01021 }
01022
01023
01030 static void WriteMapBrush (const mapbrush_t *brush, const int j, qFILE *f)
01031 {
01032 int k = 0;
01033 FS_Printf(f, "// brush %i\n{\n", j);
01034 for (k = 0; k < brush->numsides; k++) {
01035 const side_t *side = &brush->original_sides[k];
01036 const ptrdiff_t index = side - brushsides;
01037 const brush_texture_t *t = &side_brushtextures[index];
01038 const plane_t *p = &mapplanes[side->planenum];
01039 int l;
01040
01041 for (l = 0; l < 3; l++)
01042 FS_Printf(f, "( %.7g %.7g %.7g ) ", p->planeVector[l][0], p->planeVector[l][1], p->planeVector[l][2]);
01043 FS_Printf(f, "%s ", t->name);
01044 FS_Printf(f, "%.7g %.7g %.7g ", t->shift[0], t->shift[1], t->rotate);
01045 FS_Printf(f, "%.7g %.7g ", t->scale[0], t->scale[1]);
01046 FS_Printf(f, "%i %i %i\n", side->contentFlags, t->surfaceFlags, t->value);
01047 }
01048 FS_Printf(f, "}\n");
01049 }
01050
01055 void WriteMapFile (const char *filename)
01056 {
01057 qFILE f;
01058 int i, j, jc;
01059 int removed;
01060
01061 Verb_Printf(VERB_NORMAL, "writing map: '%s'\n", filename);
01062
01063 FS_OpenFile(filename, &f, FILE_WRITE);
01064 if (!f.f)
01065 Sys_Error("Could not open %s for writing", filename);
01066
01067 removed = 0;
01068 FS_Printf(&f, "\n");
01069 for (i = 0; i < num_entities; i++) {
01070 const entity_t *mapent = &entities[i];
01071 const epair_t *e = mapent->epairs;
01072
01073
01074 if (mapent->skip) {
01075 removed++;
01076 continue;
01077 }
01078 FS_Printf(&f, "// entity %i\n{\n", i - removed);
01079 WriteMapEntities(&f, e);
01080
01081
01082
01083
01084 for (j = 0, jc = 0; j < mapent->numbrushes; j++) {
01085 const mapbrush_t *brush = &mapbrushes[mapent->firstbrush + j];
01086 if (brush->skipWriteBack)
01087 continue;
01088 WriteMapBrush(brush, jc++, &f);
01089 }
01090
01091
01092 if (i == 0) {
01093 int numToAdd;
01094 mapbrush_t **brushesToAdd = Check_ExtraBrushesForWorldspawn(&numToAdd);
01095 if (brushesToAdd != NULL) {
01096 int k;
01097 for (k = 0; k < numToAdd; k++) {
01098 if (brushesToAdd[k]->skipWriteBack)
01099 continue;
01100 WriteMapBrush(brushesToAdd[k], j++, &f);
01101 }
01102 Mem_Free(brushesToAdd);
01103 }
01104 }
01105 FS_Printf(&f, "}\n");
01106 }
01107
01108 if (removed)
01109 Verb_Printf(VERB_NORMAL, "removed %i entities\n", removed);
01110 FS_CloseFile(&f);
01111 }
01112
01117 void LoadMapFile (const char *filename)
01118 {
01119 int i, subdivide;
01120
01121 Verb_Printf(VERB_EXTRA, "--- LoadMapFile ---\n");
01122
01123 LoadScriptFile(filename);
01124
01125 memset(mapbrushes, 0, sizeof(mapbrush_t) * MAX_MAP_BRUSHES);
01126 nummapbrushes = 0;
01127
01128 memset(brushsides, 0, sizeof(side_t) * MAX_MAP_SIDES);
01129 nummapbrushsides = 0;
01130
01131 memset(side_brushtextures, 0, sizeof(brush_texture_t) * MAX_MAP_SIDES);
01132
01133 memset(mapplanes, 0, sizeof(plane_t) * MAX_MAP_PLANES);
01134
01135 num_entities = 0;
01136
01137 curTile = &mapTiles.mapTiles[0];
01138
01139 mapTiles.numTiles = 1;
01140
01141 while (ParseMapEntity(filename));
01142
01143 subdivide = atoi(ValueForKey(&entities[0], "subdivide"));
01144 if (subdivide >= 256 && subdivide <= 2048) {
01145 Verb_Printf(VERB_EXTRA, "Using subdivide %d from worldspawn.\n", subdivide);
01146 config.subdivideSize = subdivide;
01147 }
01148
01149 if (footstepsCnt)
01150 Com_Printf("Generated footstep file with %i entries\n", footstepsCnt);
01151 if (materialsCnt)
01152 Com_Printf("Generated material file with %i entries\n", materialsCnt);
01153
01154 ClearBounds(map_mins, map_maxs);
01155 for (i = 0; i < entities[0].numbrushes; i++) {
01156 if (mapbrushes[i].mins[0] > MAX_WORLD_WIDTH)
01157 continue;
01158 AddPointToBounds(mapbrushes[i].mins, map_mins, map_maxs);
01159 AddPointToBounds(mapbrushes[i].maxs, map_mins, map_maxs);
01160 }
01161
01162
01163 memcpy(mapbrushes + nummapbrushes, mapbrushes, sizeof(mapbrush_t) * nummapbrushes);
01164
01165 Verb_Printf(VERB_EXTRA, "%5i brushes\n", nummapbrushes);
01166 Verb_Printf(VERB_EXTRA, "%5i total sides\n", nummapbrushsides);
01167 Verb_Printf(VERB_EXTRA, "%5i boxbevels\n", c_boxbevels);
01168 Verb_Printf(VERB_EXTRA, "%5i edgebevels\n", c_edgebevels);
01169 Verb_Printf(VERB_EXTRA, "%5i entities\n", num_entities);
01170 Verb_Printf(VERB_EXTRA, "%5i planes\n", nummapplanes);
01171 Verb_Printf(VERB_EXTRA, "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n",
01172 map_mins[0], map_mins[1], map_mins[2], map_maxs[0], map_maxs[1], map_maxs[2]);
01173 }