/** * @brief If there was an origin brush, offset all of the planes and texinfo * @note Used for e.g. func_door or func_rotating */ static void AdjustBrushesForOrigin (const entity_t* ent) { for (int i = 0; i < ent->numbrushes; i++) { mapbrush_t* b = &mapbrushes[ent->firstbrush + i]; for (int j = 0; j < b->numsides; j++) { side_t* s = &b->original_sides[j]; const ptrdiff_t index = s - brushsides; const vec_t newdist = mapplanes[s->planenum].dist - DotProduct(mapplanes[s->planenum].normal, ent->origin); s->surfaceFlags |= SURF_ORIGIN; side_brushtextures[index].surfaceFlags |= SURF_ORIGIN; s->planenum = FindOrCreateFloatPlane(mapplanes[s->planenum].normal, newdist); s->texinfo = TexinfoForBrushTexture(&mapplanes[s->planenum], &side_brushtextures[index], ent->origin, s->contentFlags & CONTENTS_TERRAIN); s->brush = b; } /* create windings for sides and bounds for brush */ MakeBrushWindings(b); } }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int TexinfoForBrushTexture(plane_t *plane, brush_texture_t *bt, vec3_t origin) { vec3_t vecs[2]; int sv, tv; vec_t ang, sinv, cosv; vec_t ns, nt; texinfo_t tx, *tc; int i, j, k; float shift[2]; brush_texture_t anim; int mt; if (!bt->name[0]) return 0; memset (&tx, 0, sizeof(tx)); strcpy (tx.texture, bt->name); TextureAxisFromPlane(plane, vecs[0], vecs[1]); shift[0] = DotProduct (origin, vecs[0]); shift[1] = DotProduct (origin, vecs[1]); if (!bt->scale[0]) bt->scale[0] = 1; if (!bt->scale[1]) bt->scale[1] = 1; // rotate axis if (bt->rotate == 0) { sinv = 0 ; cosv = 1; } else if (bt->rotate == 90) { sinv = 1 ; cosv = 0; } else if (bt->rotate == 180) { sinv = 0 ; cosv = -1; } else if (bt->rotate == 270) { sinv = -1 ; cosv = 0; } else { ang = bt->rotate / 180 * Q_PI; sinv = sin(ang); cosv = cos(ang); } if (vecs[0][0]) sv = 0; else if (vecs[0][1]) sv = 1; else sv = 2; if (vecs[1][0]) tv = 0; else if (vecs[1][1]) tv = 1; else tv = 2; for (i=0 ; i<2 ; i++) { ns = cosv * vecs[i][sv] - sinv * vecs[i][tv]; nt = sinv * vecs[i][sv] + cosv * vecs[i][tv]; vecs[i][sv] = ns; vecs[i][tv] = nt; } for (i=0 ; i<2 ; i++) for (j=0 ; j<3 ; j++) tx.vecs[i][j] = vecs[i][j] / bt->scale[i]; tx.vecs[0][3] = bt->shift[0] + shift[0]; tx.vecs[1][3] = bt->shift[1] + shift[1]; tx.flags = bt->flags; tx.value = bt->value; // // find the texinfo // tc = texinfo; for (i=0 ; i<numtexinfo ; i++, tc++) { if (tc->flags != tx.flags) continue; if (tc->value != tx.value) continue; for (j=0 ; j<2 ; j++) { if (strcmp (tc->texture, tx.texture)) goto skip; for (k=0 ; k<4 ; k++) { if (tc->vecs[j][k] != tx.vecs[j][k]) goto skip; } } return i; skip:; } *tc = tx; numtexinfo++; // load the next animation mt = FindMiptex (bt->name); if (textureref[mt].animname[0]) { anim = *bt; strcpy (anim.name, textureref[mt].animname); tc->nexttexinfo = TexinfoForBrushTexture (plane, &anim, origin); } else tc->nexttexinfo = -1; return i; } //end of the function TexinfoForBrushTexture
/* ================ ParseMapEntity ================ */ qboolean ParseMapEntity (void) { entity_t *mapent; epair_t *e; side_t *s; int i, j; int startbrush, startsides; vec_t newdist; mapbrush_t *b; if (!GetToken (true)) return false; if (strcmp (token, "{") ) Error ("ParseEntity: { not found"); if (num_entities == MAX_MAP_ENTITIES) Error ("num_entities == MAX_MAP_ENTITIES"); startbrush = nummapbrushes; startsides = nummapbrushsides; mapent = &entities[num_entities]; num_entities++; memset (mapent, 0, sizeof(*mapent)); mapent->firstbrush = nummapbrushes; mapent->numbrushes = 0; // mapent->portalareas[0] = -1; // mapent->portalareas[1] = -1; do { if (!GetToken (true)) Error ("ParseEntity: EOF without closing brace"); if (!strcmp (token, "}") ) break; if (!strcmp (token, "{") ) ParseBrush (mapent); else { e = ParseEpair (); e->next = mapent->epairs; mapent->epairs = e; } } while (1); GetVectorForKey (mapent, "origin", mapent->origin); // // if there was an origin brush, offset all of the planes and texinfo // if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) { for (i=0 ; i<mapent->numbrushes ; i++) { b = &mapbrushes[mapent->firstbrush + i]; for (j=0 ; j<b->numsides ; j++) { s = &b->original_sides[j]; newdist = mapplanes[s->planenum].dist - DotProduct (mapplanes[s->planenum].normal, mapent->origin); s->planenum = FindFloatPlane (mapplanes[s->planenum].normal, newdist); s->texinfo = TexinfoForBrushTexture (&mapplanes[s->planenum], &side_brushtextures[s-brushsides], mapent->origin); } MakeBrushWindings (b); } } // group entities are just for editor convenience // toss all brushes into the world entity if (!strcmp ("func_group", ValueForKey (mapent, "classname"))) { MoveBrushesToWorld (mapent); mapent->numbrushes = 0; return true; } // areaportal entities move their brushes, but don't eliminate // the entity if (!strcmp ("func_areaportal", ValueForKey (mapent, "classname"))) { char str[128]; if (mapent->numbrushes != 1) Error ("Entity %i: func_areaportal can only be a single brush", num_entities-1); b = &mapbrushes[nummapbrushes-1]; b->contents = CONTENTS_AREAPORTAL; c_areaportals++; mapent->areaportalnum = c_areaportals; // set the portal number as "style" sprintf (str, "%i", c_areaportals); SetKeyValue (mapent, "style", str); MoveBrushesToWorld (mapent); return true; } return true; }
/* ================= ParseBrush ================= */ void ParseBrush (entity_t *mapent) { mapbrush_t *b; int i,j, k; int mt; side_t *side, *s2; int planenum; brush_texture_t td; int planepts[3][3]; if (nummapbrushes == MAX_MAP_BRUSHES) Error ("nummapbrushes == MAX_MAP_BRUSHES"); b = &mapbrushes[nummapbrushes]; b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities-1; b->brushnum = nummapbrushes - mapent->firstbrush; do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Error ("MAX_MAP_BRUSHSIDES"); side = &brushsides[nummapbrushsides]; // read the three point plane definition for (i=0 ; i<3 ; i++) { if (i != 0) GetToken (true); if (strcmp (token, "(") ) Error ("parsing brush"); for (j=0 ; j<3 ; j++) { GetToken (false); planepts[i][j] = atoi(token); } GetToken (false); if (strcmp (token, ")") ) Error ("parsing brush"); } // // read the texturedef // GetToken (false); strcpy (td.name, token); 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); // find default flags and values mt = FindMiptex (td.name); td.flags = textureref[mt].flags; td.value = textureref[mt].value; side->contents = textureref[mt].contents; side->surf = td.flags = textureref[mt].flags; if (TokenAvailable()) { GetToken (false); side->contents = atoi(token); GetToken (false); side->surf = td.flags = atoi(token); GetToken (false); td.value = atoi(token); } // translucent objects are automatically classified as detail if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) side->contents |= CONTENTS_DETAIL; if (fulldetail) side->contents &= ~CONTENTS_DETAIL; if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|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 // planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]); if (planenum == -1) { printf ("Entity %i, Brush %i: plane with no normal\n" , b->entitynum, b->brushnum); continue; } // // see if the plane has been used already // for (k=0 ; k<b->numsides ; k++) { s2 = b->original_sides + k; if (s2->planenum == planenum) { printf ("Entity %i, Brush %i: duplicate plane\n" , b->entitynum, b->brushnum); break; } if ( s2->planenum == (planenum^1) ) { 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); // save the td off in case there is an origin brush and we // have to recalculate the texinfo side_brushtextures[nummapbrushsides] = td; nummapbrushsides++; b->numsides++; } while (1); // get the content for the entire brush b->contents = BrushContents (b); // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL) ) { b->numsides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) { b->numsides = 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_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) { c_clipbrushes++; for (i=0 ; i<b->numsides ; 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 planenums and texinfos will be adjusted for // the origin brush // if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Error ("Entity %i, Brush %i: origin brushes not allowed in world" , b->entitynum, b->brushnum); return; } VectorAdd (b->mins, b->maxs, origin); VectorScale (origin, 0.5, origin); sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue (&entities[b->entitynum], "origin", string); VectorCopy (origin, entities[b->entitynum].origin); // don't keep this brush b->numsides = 0; return; } AddBrushBevels (b); nummapbrushes++; mapent->numbrushes++; }
/* * @brief */ static _Bool ParseMapEntity(void) { entity_t *mapent; epair_t *e; side_t *s; int32_t i, j; vec_t newdist; map_brush_t *b; if (!GetToken(true)) return false; if (g_strcmp0(token, "{")) Com_Error(ERR_FATAL, "\"{\" not found\n"); if (num_entities == MAX_BSP_ENTITIES) Com_Error(ERR_FATAL, "MAX_BSP_ENTITIES\n"); mapent = &entities[num_entities]; num_entities++; memset(mapent, 0, sizeof(*mapent)); mapent->first_brush = num_map_brushes; mapent->num_brushes = 0; do { if (!GetToken(true)) Com_Error(ERR_FATAL, "EOF without closing brace\n"); if (!g_strcmp0(token, "}")) break; if (!g_strcmp0(token, "{")) ParseBrush(mapent); else { e = ParseEpair(); e->next = mapent->epairs; mapent->epairs = e; } } while (true); VectorForKey(mapent, "origin", mapent->origin); // if there was an origin brush, offset all of the planes and texinfo if (mapent->origin[0] || mapent->origin[1] || mapent->origin[2]) { for (i = 0; i < mapent->num_brushes; i++) { b = &map_brushes[mapent->first_brush + i]; for (j = 0; j < b->num_sides; j++) { s = &b->original_sides[j]; newdist = map_planes[s->plane_num].dist - DotProduct(map_planes[s->plane_num].normal, mapent->origin); s->plane_num = FindPlane(map_planes[s->plane_num].normal, newdist); s->texinfo = TexinfoForBrushTexture(&map_planes[s->plane_num], &map_brush_textures[s - map_brush_sides], mapent->origin); } MakeBrushWindings(b); } } // group entities are just for editor convenience // toss all brushes into the world entity if (!g_strcmp0("func_group", ValueForKey(mapent, "classname"))) { MoveBrushesToWorld(mapent); mapent->num_brushes = 0; return true; } // areaportal entities move their brushes, but don't eliminate the entity if (!g_strcmp0("func_areaportal", ValueForKey(mapent, "classname"))) { char str[128]; if (mapent->num_brushes != 1) Com_Error( ERR_FATAL, "ParseMapEntity: %i func_areaportal can only be a single brush\n", num_entities - 1); b = &map_brushes[num_map_brushes - 1]; b->contents = CONTENTS_AREA_PORTAL; c_area_portals++; mapent->area_portal_num = c_area_portals; // set the portal number as "style" sprintf(str, "%i", c_area_portals); SetKeyValue(mapent, "areaportal", str); MoveBrushesToWorld(mapent); return true; } return true; }
/* * @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++; }
/* ================= Q2_ParseBrush ================= */ void Q2_ParseBrush(script_t *script, entity_t *mapent) { mapbrush_t *b; int i, j, k; int mt; side_t *side, *s2; int planenum; brush_texture_t td; int planepts[3][3]; token_t token; if (nummapbrushes >= MAX_MAPFILE_BRUSHES) { Error("nummapbrushes == MAX_MAPFILE_BRUSHES"); } b = &mapbrushes[nummapbrushes]; b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities - 1; b->brushnum = nummapbrushes - mapent->firstbrush; b->leafnum = -1; do { if (!PS_ReadToken(script, &token)) { break; } if (!strcmp(token.string, "}")) { break; } //IDBUG: mixed use of MAX_MAPFILE_? and MAX_MAP_? this could // lead to out of bound indexing of the arrays if (nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES) { Error("MAX_MAPFILE_BRUSHSIDES"); } side = &brushsides[nummapbrushsides]; //read the three point plane definition for (i = 0; i < 3; i++) { if (i != 0) { PS_ExpectTokenString(script, "("); } for (j = 0; j < 3; j++) { PS_ExpectAnyToken(script, &token); planepts[i][j] = atof(token.string); } //end for PS_ExpectTokenString(script, ")"); } //end for // //read the texturedef // PS_ExpectAnyToken(script, &token); strcpy(td.name, token.string); PS_ExpectAnyToken(script, &token); td.shift[0] = atol(token.string); PS_ExpectAnyToken(script, &token); td.shift[1] = atol(token.string); PS_ExpectAnyToken(script, &token); td.rotate = atol(token.string); PS_ExpectAnyToken(script, &token); td.scale[0] = atof(token.string); PS_ExpectAnyToken(script, &token); td.scale[1] = atof(token.string); //find default flags and values mt = FindMiptex(td.name); td.flags = textureref[mt].flags; td.value = textureref[mt].value; side->contents = textureref[mt].contents; side->surf = td.flags = textureref[mt].flags; //check if there's a number available if (PS_CheckTokenType(script, TT_NUMBER, 0, &token)) { side->contents = token.intvalue; PS_ExpectTokenType(script, TT_NUMBER, 0, &token); side->surf = td.flags = token.intvalue; PS_ExpectTokenType(script, TT_NUMBER, 0, &token); td.value = token.intvalue; } // translucent objects are automatically classified as detail // if (side->surf & (SURF_TRANS33|SURF_TRANS66) ) // side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP)) { side->contents |= CONTENTS_DETAIL; } if (fulldetail) { side->contents &= ~CONTENTS_DETAIL; } if (!(side->contents & ((LAST_VISIBLE_CONTENTS - 1) | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | 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; // } #ifdef ME //for creating AAS... this side is textured side->flags |= SFL_TEXTURED; #endif //ME // // find the plane number // planenum = PlaneFromPoints(planepts[0], planepts[1], planepts[2]); if (planenum == -1) { Log_Print("Entity %i, Brush %i: plane with no normal\n" , b->entitynum, b->brushnum); continue; } // // see if the plane has been used already // for (k = 0 ; k < b->numsides ; k++) { s2 = b->original_sides + k; if (s2->planenum == planenum) { Log_Print("Entity %i, Brush %i: duplicate plane\n" , b->entitynum, b->brushnum); break; } if (s2->planenum == (planenum ^ 1)) { Log_Print("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); // save the td off in case there is an origin brush and we // have to recalculate the texinfo side_brushtextures[nummapbrushsides] = td; nummapbrushsides++; b->numsides++; } while (1); // get the content for the entire brush b->contents = Q2_BrushContents(b); #ifdef ME if (BrushExists(b)) { c_squattbrushes++; b->numsides = 0; return; } //end if if (create_aas) { //create AAS brushes, and add brush bevels AAS_CreateMapBrushes(b, mapent, true); //NOTE: if we return here then duplicate plane errors occur for the non world entities return; } //end if #endif //ME // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL)) { b->numsides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER))) { b->numsides = 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_PLAYERCLIP | CONTENTS_MONSTERCLIP)) { c_clipbrushes++; for (i = 0 ; i < b->numsides ; 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 planenums and texinfos will be adjusted for // the origin brush // if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Error("Entity %i, Brush %i: origin brushes not allowed in world" , b->entitynum, b->brushnum); return; } VectorAdd(b->mins, b->maxs, origin); VectorScale(origin, 0.5, origin); sprintf(string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue(&entities[b->entitynum], "origin", string); VectorCopy(origin, entities[b->entitynum].origin); // don't keep this brush b->numsides = 0; return; } AddBrushBevels(b); nummapbrushes++; mapent->numbrushes++; }
/* ================= ParseBrush ================= */ void ParseBrush (entity_t *mapent) { char *tl; mapbrush_t *b; int i,j, k; int mt; side_t *side, *s2; int planenum; brush_texture_t td; float planepts[3][3]; qboolean phongShading; MarkBrushBegin(); if (nummapbrushes == MAX_MAP_BRUSHES) Error ("nummapbrushes == MAX_MAP_BRUSHES"); b = &mapbrushes[nummapbrushes]; b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = num_entities-1; b->brushnum = nummapbrushes - mapent->firstbrush; // Knightmare added b->optimizedDetail = false; b->isTerrain = (!strcmp("func_group", ValueForKey (&entities[b->entitynum], "classname")) && strlen(ValueForKey (&entities[b->entitynum], "terrain")) > 0); b->isGenSurf = (!strcmp("func_group", ValueForKey (&entities[b->entitynum], "classname")) && strlen(ValueForKey (&entities[b->entitynum], "gensurf")) > 0); phongShading = (!strcmp("func_group", ValueForKey (&entities[b->entitynum], "classname")) && strlen(ValueForKey (&entities[b->entitynum], "phongshading")) > 0); //if (b->isTerrain) // printf ("Brush number %i in enitity number %i has terrain flag set.\n", b->brushnum, b->entitynum); //if (b->phongShading) // printf ("Brush number %i in enitity number %i has phong shading flag set.\n", b->brushnum, b->entitynum); if (verbosebrushes) printf ("enitity %i, brush %i\n", b->entitynum, b->brushnum); // end Knightmare do { if (!GetToken (true)) break; if (!strcmp (token, "}") ) break; if (nummapbrushsides == MAX_MAP_BRUSHSIDES) Error ("MAX_MAP_BRUSHSIDES"); side = &brushsides[nummapbrushsides]; // read the three point plane definition for (i=0 ; i<3 ; i++) { if (i != 0) GetToken (true); if (strcmp (token, "(") ) Error ("parsing brush\n Brush: %s", i+1, brush_info); for (j=0 ; j<3 ; j++) { GetToken (false); planepts[i][j] = atof(token); } GetToken (false); if (strcmp (token, ")") ) Error ("parsing brush\n Brush: %s", i+1, brush_info); } // // read the texturedef // GetToken (false); strcpy (td.name, token); tl = td.name; for(tl = td.name; *tl != 0; tl++) *tl = tolower(*tl); 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); // find default flags and values mt = FindMiptex (td.name); td.flags = textureref[mt].flags; td.value = textureref[mt].value; side->contents = textureref[mt].contents; side->surf = td.flags = textureref[mt].flags; if (TokenAvailable()) { GetToken (false); side->contents = atoi(token); GetToken (false); side->surf = td.flags = atoi(token); GetToken (false); td.value = atoi(token); } // translucent objects are automatically classified as detail if (side->surf & (SURF_TRANS33|SURF_TRANS66|SURF_ALPHATEST) ) // Knightmare- added alphatest side->contents |= CONTENTS_DETAIL; if (side->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) side->contents |= CONTENTS_DETAIL; if (fulldetail) side->contents &= ~CONTENTS_DETAIL; if (!(side->contents & ((LAST_VISIBLE_CONTENTS-1) | CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP|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; side->contents &= CONTENTS_DETAIL; // allow only detail content- Geoffery DeWan? } // // find the plane number // planenum = PlaneFromPoints (planepts[0], planepts[1], planepts[2]); if (planenum == -1) { printf ("Entity %i, Brush %i: plane with no normal\n Brush: %s\n" , b->entitynum, b->brushnum, brush_info); continue; } // // see if the plane has been used already // for (k=0 ; k<b->numsides ; k++) { s2 = b->original_sides + k; if (s2->planenum == planenum) { printf ("Entity %i, Brush %i: duplicate plane\n Brush: %s\n" , b->entitynum, b->brushnum, brush_info); break; } if ( s2->planenum == (planenum^1) ) { printf ("Entity %i, Brush %i: mirrored plane\n Brush: %s\n" , b->entitynum, b->brushnum, brush_info); 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, b->isTerrain); // Knightmare added // save the td off in case there is an origin brush and we // have to recalculate the texinfo side_brushtextures[nummapbrushsides] = td; nummapbrushsides++; b->numsides++; } while (1); // get the content for the entire brush b->contents = BrushContents (b); // allow detail brushes to be removed if (nodetail && (b->contents & CONTENTS_DETAIL) ) { b->numsides = 0; return; } // allow water brushes to be removed if (nowater && (b->contents & (CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER)) ) { b->numsides = 0; return; } // Knightmare- check if this is an optimized detail brush (has caulk faces) // also exclude trans brushes and terrain if ((b->contents & CONTENTS_DETAIL) && (b->contents & CONTENTS_SOLID) && !b->isTerrain && !b->isGenSurf) for (i=0; i<b->numsides; i++) { if ( !strcmp(texinfo[b->original_sides[i].texinfo].texture, "system/caulk") || !strcmp(texinfo[b->original_sides[i].texinfo].texture, "common/caulk") ) { b->optimizedDetail = true; //printf ("Entity %i, Brush %i: optimized detail\n", b->entitynum, b->brushnum); break; } } // Knightmare- special handling for terrain brushes if (b->isTerrain || b->isGenSurf || phongShading) for (i=0; i<b->numsides; i++) { s2 = &b->original_sides[i]; // set ArghRad phong shading value (because EasyGen/GTKGenSurf doesn't allow this) if (strcmp(texinfo[b->original_sides[i].texinfo].texture, "system/caulk") && strcmp(texinfo[b->original_sides[i].texinfo].texture, "common/caulk")) { texinfo[s2->texinfo].value = 777 + b->entitynum; // lucky 7's texinfo[s2->texinfo].flags &= ~SURF_LIGHT; // must not be light-emitting } } // create windings for sides and bounds for brush // MEM_LEAK MakeBrushWindings (b); t_w = b->original_sides[0].winding; // brushes that will not be visible at all will never be // used as bsp splitters if (b->contents & (CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP) ) { c_clipbrushes++; for (i=0 ; i<b->numsides ; 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 planenums and texinfos will be adjusted for // the origin brush // if (b->contents & CONTENTS_ORIGIN) { char string[32]; vec3_t origin; if (num_entities == 1) { Error ("Entity %i, Brush %i: origin brushes not allowed in world\n BrushL %s", b->entitynum, b->brushnum, brush_info); return; } VectorAdd (b->mins, b->maxs, origin); VectorScale (origin, 0.5, origin); sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); SetKeyValue (&entities[b->entitynum], "origin", string); VectorCopy (origin, entities[b->entitynum].origin); // don't keep this brush b->numsides = 0; return; } AddBrushBevels (b); nummapbrushes++; mapent->numbrushes++; }
/** * @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++; }
/* =========== MakeBrushPlanes =========== */ qboolean MakeBrushPlanes (brush_t *b) { int i, j; int planenum; side_t *s; int contents; bface_t *f; vec3_t origin; // // if the origin key is set (by an origin brush), offset all of the values // GetVectorForKey (&entities[b->entitynum], "origin", origin); // // convert to mapplanes // for (i=0 ; i<b->numsides ; i++) { s = &brushsides[b->firstside + i]; for (j=0 ; j<3 ; j++) { VectorSubtract (s->planepts[j], origin, s->planepts[j]); } planenum = PlaneFromPoints (s->planepts[0], s->planepts[1], s->planepts[2]); if (planenum == -1) { printf ("Entity %i, Brush %i: plane with no normal\n" , b->entitynum, b->brushnum); continue; } // // see if the plane has been used already // for (f=b->hulls[0].faces ; f ; f=f->next) { if (f->planenum == planenum || f->planenum == (planenum^1) ) { char *pszClass = "Unknown Class"; if ( b->entitynum ) { entity_t *e = entities + b->entitynum; pszClass = ValueForKey(e, "classname" ); } printf( "Entity %i, Brush %i: A '%s' @(%.0f,%.0f,%.0f) has a coplanar plane at (%.0f, %.0f, %.0f), texture %s\n", b->entitynum, b->brushnum, pszClass, origin[0], origin[1], origin[2], s->planepts[0][0]+origin[0], s->planepts[0][1]+origin[1], s->planepts[0][2]+origin[2], s->td.name ); return false; } } f = malloc(sizeof(*f)); memset (f, 0, sizeof(*f)); f->planenum = planenum; f->plane = &mapplanes[planenum]; f->next = b->hulls[0].faces; b->hulls[0].faces = f; f->texinfo = onlyents ? 0 : TexinfoForBrushTexture (f->plane, &s->td, origin); } return true; }