map.c

Go to the documentation of this file.
00001 
00005 /*
00006 Copyright (C) 1997-2001 Id Software, Inc.
00007 
00008 This program is free software; you can redistribute it and/or
00009 modify it under the terms of the GNU General Public License
00010 as published by the Free Software Foundation; either version 2
00011 of the License, or (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00016 
00017 See the GNU General Public License for more details.
00018 
00019 You should have received a copy of the GNU General Public License
00020 along with this program; if not, write to the Free Software
00021 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
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 PLANE FINDING
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     /* NOTE: should these have an epsilon around 1.0? */
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     /* create a new plane */
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     /* always put axial planes facing positive first */
00139     if (AXIAL(p)) {
00140         if (p->normal[0] < 0 || p->normal[1] < 0 || p->normal[2] < 0) {
00141             /* flip order by swapping the planes */
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     /* search the border bins as well */
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     /* add the axial planes */
00295     order = 0;
00296     for (axis = 0; axis < 3; axis++) {
00297         for (dir = -1; dir <= 1; dir += 2, order++) {
00298             side_t *s;
00299             /* see if the plane is already present */
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) { /* add a new side */
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             /* if the plane is not in it canonical order, swap it */
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     /* add the edge bevels */
00340     if (b->numsides == 6)
00341         return;     /* pure axial */
00342 
00343     /* test the non-axial plane edges */
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;  /* axial */
00364             if (k != 3)
00365                 continue;   /* only test non-axial edges */
00366 
00367             /* try the six possible slanted axials from this edge */
00368             for (axis = 0; axis < 3; axis++) {
00369                 for (dir = -1; dir <= 1; dir += 2) {
00370                     /* construct a plane */
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                     /* if all the points on all the sides are
00382                      * behind this plane, it is a proper edge bevel */
00383                     for (k = 0; k < b->numsides; k++) {
00384                         winding_t *w2;
00385                         float minBack;
00386 
00387                         /* @note: This leads to different results on different archs
00388                          * due to float rounding/precision errors - use the -ffloat-store
00389                          * feature of gcc to 'fix' this */
00390                         /* if this plane has already been used, skip it */
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;  /* point in front */
00402                             if (d < minBack)
00403                                 minBack = d;
00404                         }
00405                         /* if some point was at the front */
00406                         if (l != w2->numpoints)
00407                             break;
00408                         /* if no points at the back then the winding is on the bevel plane */
00409                         if (minBack > -0.1f)
00410                             break;
00411                     }
00412 
00413                     if (k != b->numsides)
00414                         continue;   /* wasn't part of the outer hull */
00415                     /* add this plane */
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             /* back side clipaway */
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); /*CLIP_EPSILON); */
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     /* we already have a material definition for this texture */
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     /* envmap for water surfaces */
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         /* read the three point plane definition */
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         /* read the texturedef */
00655         GetToken(qfalse);
00656         if (strlen(parsedToken) >= MAX_TEXPATH) {
00657             if (config.performMapCheck || config.fixMap)
00658                 Com_Printf("  ");/* hack to make this look like output from Check_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         /* find default flags and values */
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         /* if in check or fix mode, let them choose to do this (with command line options),
00690          * and then call is made elsewhere */
00691         if (!checkOrFix) {
00692             SetImpliedFlags(side, &td, b);
00693             /* if no other content flags are set - make this solid */
00694             if (!checkOrFix && side->contentFlags == 0)
00695                 side->contentFlags = CONTENTS_SOLID;
00696         }
00697 
00698         /* translucent objects are automatically classified as detail and window */
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             /* hints and skips are never detail, and have no content */
00713             if (side->surfaceFlags & (SURF_HINT | SURF_SKIP)) {
00714                 side->contentFlags = 0;
00715                 side->surfaceFlags &= ~CONTENTS_DETAIL;
00716             }
00717         }
00718 
00719         /* check whether the flags are ok */
00720         CheckFlags(side, b);
00721 
00722         /* generate a list of textures that should have footsteps when walking on them */
00723         if (mt > 0 && side->surfaceFlags & SURF_FOOTSTEP)
00724             GenerateFootstepList(filename, mt);
00725         GenerateMaterialFile(filename, mt, side);
00726 
00727         /* find the plane number */
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         /* see if the plane has been used already */
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;       /* duplicated */
00751 
00752         /* keep this side */
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         /* save the td off in case there is an origin brush and we
00760          * have to recalculate the texinfo */
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     /* get the content for the entire brush */
00774     b->contentFlags = BrushContents(b);
00775 
00776     /* copy all set face contentflags to the brush contentflags */
00777     for (m = 0; m < b->numsides; m++)
00778         b->contentFlags |= b->original_sides[m].contentFlags;
00779 
00780     /* set DETAIL, TRANSLUCENT contentflags on all faces, if they have been set on any.
00781      * called separately, if in check/fix mode */
00782     if (!checkOrFix)
00783         CheckPropagateParserContentFlags(b);
00784 
00785     /* allow detail brushes to be removed */
00786     if (config.nodetail && (b->contentFlags & CONTENTS_DETAIL)) {
00787         b->numsides = 0;
00788         return;
00789     }
00790 
00791     /* allow water brushes to be removed */
00792     if (config.nowater && (b->contentFlags & CONTENTS_WATER)) {
00793         b->numsides = 0;
00794         return;
00795     }
00796 
00797     /* create windings for sides and bounds for brush */
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     /* origin brushes are removed, but they set
00805      * the rotation origin for the rest of the brushes (like func_door)
00806      * in the entity. After the entire entity is parsed, the planenums
00807      * and texinfos will be adjusted for the origin brush */
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         /* don't keep this brush */
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     /* this is pretty gross, because the brushes are expected to be
00853      * in linear order for each entity */
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     /* make space to move the brushes (overlapped copy) */
00865     memmove(mapbrushes + worldbrushes + newbrushes,
00866         mapbrushes + worldbrushes,
00867         sizeof(mapbrush_t) * (nummapbrushes - worldbrushes - newbrushes));
00868 
00869     /* copy the new brushes down */
00870     memcpy(mapbrushes + worldbrushes, temp, sizeof(*temp) * newbrushes);
00871 
00872     /* fix up indexes */
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         /* create windings for sides and bounds for brush */
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     /* group entities are just for editor convenience
00983      * toss all brushes into the world entity */
00984     entName = ValueForKey(mapent, "classname");
00985 
00986     /* offset all of the planes and texinfo if needed */
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         /* maybe we don't want to write it back into the file */
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         /* need 2 counters. j counts the brushes in the source entity.
01082          * jc counts the brushes written back. they may differ if some are skipped,
01083          * eg they are microbrushes */
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         /* add brushes from func_groups with single members to worldspawn */
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     /* Create this shortcut to mapTiles[0] */
01137     curTile = &mapTiles.mapTiles[0];
01138     /* Set the number of tiles to 1. This is fix for ufo2map right now. */
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;   /* no valid points */
01158         AddPointToBounds(mapbrushes[i].mins, map_mins, map_maxs);
01159         AddPointToBounds(mapbrushes[i].maxs, map_mins, map_maxs);
01160     }
01161 
01162     /* save a copy of the brushes */
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 }

Generated by  doxygen 1.6.2