/* * @brief */ static void ParseBrush(entity_t *mapent) { map_brush_t *b; int32_t i, j, k; side_t *side, *s2; int32_t plane_num; map_brush_texture_t td; vec3_t planepts[3]; if (num_map_brushes == MAX_BSP_BRUSHES) Com_Error(ERR_FATAL, "MAX_BSP_BRUSHES\n"); b = &map_brushes[num_map_brushes]; b->original_sides = &map_brush_sides[num_map_brush_sides]; b->entity_num = num_entities - 1; b->brush_num = num_map_brushes - mapent->first_brush; do { if (!GetToken(true)) break; if (!g_strcmp0(token, "}")) break; if (num_map_brush_sides == MAX_BSP_BRUSH_SIDES) Com_Error(ERR_FATAL, "MAX_BSP_BRUSH_SIDES\n"); side = &map_brush_sides[num_map_brush_sides]; // read the three point plane definition for (i = 0; i < 3; i++) { if (i != 0) GetToken(true); if (g_strcmp0(token, "(")) Com_Error(ERR_FATAL, "Parsing brush\n"); for (j = 0; j < 3; j++) { GetToken(false); planepts[i][j] = atof(token); } GetToken(false); if (g_strcmp0(token, ")")) Com_Error(ERR_FATAL, "Parsing brush\n"); } memset(&td, 0, sizeof(td)); // read the texturedef GetToken(false); if (strlen(token) > sizeof(td.name) - 1) Com_Error(ERR_FATAL, "Texture name \"%s\" is too long.\n", token); g_strlcpy(td.name, token, sizeof(td.name)); GetToken(false); td.shift[0] = atoi(token); GetToken(false); td.shift[1] = atoi(token); GetToken(false); td.rotate = atoi(token); GetToken(false); td.scale[0] = atof(token); GetToken(false); td.scale[1] = atof(token); if (TokenAvailable()) { GetToken(false); side->contents = atoi(token); GetToken(false); side->surf = td.flags = atoi(token); GetToken(false); td.value = atoi(token); } else { side->contents = CONTENTS_SOLID; side->surf = td.flags = 0; td.value = 0; } // resolve implicit surface and contents flags SetImpliedFlags(side, td.name); // translucent objects are automatically classified as detail if (side->surf & (SURF_ALPHA_TEST | SURF_BLEND_33 | SURF_BLEND_66)) side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP)) side->contents |= CONTENTS_DETAIL; if (fulldetail) side->contents &= ~CONTENTS_DETAIL; if (!(side->contents & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP | CONTENTS_MIST))) side->contents |= CONTENTS_SOLID; // hints and skips are never detail, and have no content if (side->surf & (SURF_HINT | SURF_SKIP)) { side->contents = 0; side->surf &= ~CONTENTS_DETAIL; } // find the plane number plane_num = PlaneFromPoints(planepts[0], planepts[1], planepts[2]); if (plane_num == -1) { Com_Verbose("Entity %i, Brush %i: plane with no normal\n", b->entity_num, b->brush_num); continue; } // see if the plane has been used already for (k = 0; k < b->num_sides; k++) { s2 = b->original_sides + k; if (s2->plane_num == plane_num) { Com_Verbose("Entity %i, Brush %i: duplicate plane\n", b->entity_num, b->brush_num); break; } if (s2->plane_num == (plane_num ^ 1)) { Com_Verbose("Entity %i, Brush %i: mirrored plane\n", b->entity_num, b->brush_num); break; } } if (k != b->num_sides) continue; // duplicated // keep this side side = b->original_sides + b->num_sides; side->plane_num = plane_num; side->texinfo = TexinfoForBrushTexture(&map_planes[plane_num], &td, vec3_origin); // save the td off in case there is an origin brush and we // have to recalculate the texinfo map_brush_textures[num_map_brush_sides] = td; num_map_brush_sides++; b->num_sides++; } while (true); // get the content for the entire brush b->contents = BrushContents(b); // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL)) { b->num_sides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & MASK_LIQUID)) { b->num_sides = 0; return; } // create windings for sides and bounds for brush MakeBrushWindings(b); // brushes that will not be visible at all will never be // used as bsp splitters if (b->contents & (CONTENTS_PLAYER_CLIP | CONTENTS_MONSTER_CLIP)) { c_clip_brushes++; for (i = 0; i < b->num_sides; i++) b->original_sides[i].texinfo = TEXINFO_NODE; } // origin brushes are removed, but they set // the rotation origin for the rest of the brushes // in the entity. After the entire entity is parsed, // the plane_nums and texinfos will be adjusted for // the origin brush if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Com_Error(ERR_FATAL, "Entity %i, Brush %i: origin brushes not allowed in world\n", b->entity_num, b->brush_num); return; } VectorAdd(b->mins, b->maxs, origin); VectorScale(origin, 0.5, origin); sprintf(string, "%i %i %i", (int32_t)origin[0], (int32_t)origin[1], (int32_t)origin[2]); SetKeyValue(&entities[b->entity_num], "origin", string); VectorCopy(origin, entities[b->entity_num].origin); // don't keep this brush b->num_sides = 0; return; } AddBrushBevels(b); num_map_brushes++; mapent->num_brushes++; }
/** * @brief Parses a brush from the map file * @sa FindMiptex * @param[in] mapent The entity the brush to parse belongs to * @param[in] filename The map filename, used to derive the name for the footsteps file */ static void ParseBrush (entity_t* mapent, const char* filename) { int j, k; brush_texture_t td; vec3_t planepts[3]; const int checkOrFix = config.performMapCheck || config.fixMap ; if (nummapbrushes == MAX_MAP_BRUSHES) Sys_Error("nummapbrushes == MAX_MAP_BRUSHES (%i)", nummapbrushes); mapbrush_t* b = &mapbrushes[nummapbrushes]; OBJZERO(*b); b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities - 1; b->brushnum = nummapbrushes - mapent->firstbrush; do { if (Q_strnull(GetToken())) break; if (*parsedToken == '}') break; if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Sys_Error("nummapbrushsides == MAX_MAP_BRUSHSIDES (%i)", nummapbrushsides); side_t* side = &brushsides[nummapbrushsides]; /* read the three point plane definition */ for (int i = 0; i < 3; i++) { if (i != 0) GetToken(); if (*parsedToken != '(') Sys_Error("parsing brush"); for (j = 0; j < 3; j++) { GetToken(); planepts[i][j] = atof(parsedToken); } GetToken(); if (*parsedToken != ')') Sys_Error("parsing brush"); } /* read the texturedef */ GetToken(); if (strlen(parsedToken) >= MAX_TEXPATH) { if (config.performMapCheck || config.fixMap) Com_Printf(" ");/* hack to make this look like output from Check_Printf() */ Com_Printf("ParseBrush: texture name too long (limit %i): %s\n", MAX_TEXPATH, parsedToken); if (config.fixMap) Sys_Error("Aborting, as -fix is active and saving might corrupt *.map by truncating texture name"); } Q_strncpyz(td.name, parsedToken, sizeof(td.name)); td.shift[0] = atof(GetToken()); td.shift[1] = atof(GetToken()); td.rotate = atof(GetToken()); td.scale[0] = atof(GetToken()); td.scale[1] = atof(GetToken()); /* find default flags and values */ const int mt = FindMiptex(td.name); side->surfaceFlags = td.surfaceFlags = side->contentFlags = td.value = 0; if (TokenAvailable()) { side->contentFlags = atoi(GetToken()); side->surfaceFlags = td.surfaceFlags = atoi(GetToken()); td.value = atoi(GetToken()); } /* if in check or fix mode, let them choose to do this (with command line options), * and then call is made elsewhere */ if (!checkOrFix) { SetImpliedFlags(side, &td, b); /* if no other content flags are set - make this solid */ if (!checkOrFix && side->contentFlags == 0) side->contentFlags = CONTENTS_SOLID; } /* translucent objects are automatically classified as detail and window */ if (side->surfaceFlags & (SURF_BLEND33 | SURF_BLEND66 | SURF_ALPHATEST)) { side->contentFlags |= CONTENTS_DETAIL; side->contentFlags |= CONTENTS_TRANSLUCENT; side->contentFlags |= CONTENTS_WINDOW; side->contentFlags &= ~CONTENTS_SOLID; } if (config.fulldetail) side->contentFlags &= ~CONTENTS_DETAIL; if (!checkOrFix) { if (!(side->contentFlags & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_ACTORCLIP | CONTENTS_WEAPONCLIP | CONTENTS_LIGHTCLIP))) side->contentFlags |= CONTENTS_SOLID; /* hints and skips are never detail, and have no content */ if (side->surfaceFlags & (SURF_HINT | SURF_SKIP)) { side->contentFlags = 0; side->surfaceFlags &= ~CONTENTS_DETAIL; } } /* check whether the flags are ok */ CheckFlags(side, b); /* generate a list of textures that should have footsteps when walking on them */ if (mt > 0 && (side->surfaceFlags & SURF_FOOTSTEP)) GenerateFootstepList(filename, mt); GenerateMaterialFile(filename, mt, side); /* find the plane number */ int planenum = PlaneFromPoints(b, planepts[0], planepts[1], planepts[2]); if (planenum == PLANENUM_LEAF) { Com_Printf("Entity %i, Brush %i: plane with no normal\n", b->entitynum, b->brushnum); continue; } for (j = 0; j < 3; j++) VectorCopy(planepts[j], mapplanes[planenum].planeVector[j]); /* see if the plane has been used already */ for (k = 0; k < b->numsides; k++) { const side_t* s2 = b->original_sides + k; if (s2->planenum == planenum) { Com_Printf("Entity %i, Brush %i: duplicate plane\n", b->entitynum, b->brushnum); break; } if (s2->planenum == (planenum ^ 1)) { Com_Printf("Entity %i, Brush %i: mirrored plane\n", b->entitynum, b->brushnum); break; } } if (k != b->numsides) continue; /* duplicated */ /* keep this side */ side = b->original_sides + b->numsides; side->planenum = planenum; side->texinfo = TexinfoForBrushTexture(&mapplanes[planenum], &td, vec3_origin, side->contentFlags & CONTENTS_TERRAIN); side->brush = b; /* save the td off in case there is an origin brush and we * have to recalculate the texinfo */ side_brushtextures[nummapbrushsides] = td; 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, planepts[0][0], planepts[0][1], planepts[0][2], planepts[1][0], planepts[1][1], planepts[1][2], planepts[2][0], planepts[2][1], planepts[2][2], side->texinfo, td.name, planenum); nummapbrushsides++; b->numsides++; } while (1); /* get the content for the entire brush */ b->contentFlags = BrushContents(b); /* copy all set face contentflags to the brush contentflags */ for (int m = 0; m < b->numsides; m++) b->contentFlags |= b->original_sides[m].contentFlags; /* set DETAIL, TRANSLUCENT contentflags on all faces, if they have been set on any. * called separately, if in check/fix mode */ if (!checkOrFix) CheckPropagateParserContentFlags(b); /* allow detail brushes to be removed */ if (config.nodetail && (b->contentFlags & CONTENTS_DETAIL)) { b->numsides = 0; return; } /* allow water brushes to be removed */ if (config.nowater && (b->contentFlags & CONTENTS_WATER)) { b->numsides = 0; return; } /* create windings for sides and bounds for brush */ MakeBrushWindings(b); Verb_Printf(VERB_DUMP, "Brush %i mins (%f %f %f) maxs (%f %f %f)\n", nummapbrushes, b->mbBox.mins[0], b->mbBox.mins[1], b->mbBox.mins[2], b->mbBox.maxs[0], b->mbBox.maxs[1], b->mbBox.maxs[2]); /* origin brushes are removed, but they set * the rotation origin for the rest of the brushes (like func_door) * in the entity. After the entire entity is parsed, the planenums * and texinfos will be adjusted for the origin brush */ if (!checkOrFix && (b->contentFlags & CONTENTS_ORIGIN)) { char string[32]; vec3_t origin; if (num_entities == 1) { Sys_Error("Entity %i, Brush %i: origin brushes not allowed in world" , b->entitynum, b->brushnum); return; } b->mbBox.getCenter(origin); Com_sprintf(string, sizeof(string), "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue(&entities[b->entitynum], "origin", string); Verb_Printf(VERB_EXTRA, "Entity %i, Brush %i: set origin to %s\n", b->entitynum, b->brushnum, string); VectorCopy(origin, entities[b->entitynum].origin); /* don't keep this brush */ b->numsides = 0; return; } if (!checkOrFix) AddBrushBevels(b); nummapbrushes++; mapent->numbrushes++; }