/* =========================================================================== Return to Castle Wolfenstein single player GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). RTCW SP Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. RTCW SP Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with RTCW SP Source Code. If not, see . In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ //=========================================================================== // // Name: map_q2.c // Function: map loading and writing // Programmer: Mr Elusive (MrElusive@demigod.demon.nl) // Last update: 1997-12-03 // Tab Size: 3 //=========================================================================== //=========================================================================== // ANSI, Area Navigational System Interface // AAS, Area Awareness System //=========================================================================== #include "qbsp.h" #include "l_mem.h" #include "../botlib/aasfile.h" //aas_bbox_t #include "aas_store.h" //AAS_MAX_BBOXES #include "aas_cfg.h" #include "aas_map.h" //AAS_CreateMapBrushes #include "l_bsp_q2.h" #ifdef ME #define NODESTACKSIZE 1024 int nodestack[NODESTACKSIZE]; int *nodestackptr; int nodestacksize = 0; int brushmodelnumbers[MAX_MAPFILE_BRUSHES]; int dbrushleafnums[MAX_MAPFILE_BRUSHES]; int dplanes2mapplanes[MAX_MAPFILE_PLANES]; #endif //ME //==================================================================== //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q2_CreateMapTexinfo( void ) { int i; for ( i = 0; i < numtexinfo; i++ ) { memcpy( map_texinfo[i].vecs, texinfo[i].vecs, sizeof( float ) * 2 * 4 ); map_texinfo[i].flags = texinfo[i].flags; map_texinfo[i].value = texinfo[i].value; strcpy( map_texinfo[i].texture, texinfo[i].texture ); map_texinfo[i].nexttexinfo = 0; } //end for } //end of the function Q2_CreateMapTexinfo /* =========== Q2_BrushContents =========== */ int Q2_BrushContents( mapbrush_t *b ) { int contents; side_t *s; int i; int trans; s = &b->original_sides[0]; contents = s->contents; trans = texinfo[s->texinfo].flags; for ( i = 1; i < b->numsides; i++, s++ ) { s = &b->original_sides[i]; trans |= texinfo[s->texinfo].flags; if ( s->contents != contents ) { Log_Print( "Entity %i, Brush %i: mixed face contents\n" , b->entitynum, b->brushnum ); Log_Print( "texture name = %s\n", texinfo[s->texinfo].texture ); break; } } // if any side is translucent, mark the contents // and change solid to window if ( trans & ( SURF_TRANS33 | SURF_TRANS66 ) ) { contents |= CONTENTS_Q2TRANSLUCENT; if ( contents & CONTENTS_SOLID ) { contents &= ~CONTENTS_SOLID; contents |= CONTENTS_WINDOW; } } return contents; } #ifdef ME //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void MakeAreaPortalBrush( mapbrush_t *brush ) { int sn; side_t *s; brush->contents = CONTENTS_AREAPORTAL; for ( sn = 0; sn < brush->numsides; sn++ ) { s = brush->original_sides + sn; //make sure the surfaces are not hint or skip s->surf &= ~( SURF_HINT | SURF_SKIP ); // s->texinfo = 0; s->contents = CONTENTS_AREAPORTAL; } //end for } //end of the function MakeAreaPortalBrush //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void DPlanes2MapPlanes( void ) { int i; for ( i = 0; i < numplanes; i++ ) { dplanes2mapplanes[i] = FindFloatPlane( dplanes[i].normal, dplanes[i].dist ); } //end for } //end of the function DPlanes2MapPlanes //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void MarkVisibleBrushSides( mapbrush_t *brush ) { int n, i, planenum; side_t *side; dface_t *face; // for ( n = 0; n < brush->numsides; n++ ) { side = brush->original_sides + n; //if this side is a bevel or the leaf number of the brush is unknown if ( ( side->flags & SFL_BEVEL ) || brush->leafnum < 0 ) { //this side is a valid splitter side->flags |= SFL_VISIBLE; continue; } //end if //assum this side will not be used as a splitter side->flags &= ~SFL_VISIBLE; //check if the side plane is used by a visible face for ( i = 0; i < numfaces; i++ ) { face = &dfaces[i]; planenum = dplanes2mapplanes[face->planenum]; if ( ( planenum & ~1 ) == ( side->planenum & ~1 ) ) { //this side is a valid splitter side->flags |= SFL_VISIBLE; } //end if } //end for } //end for } //end of the function MarkVisibleBrushSides #endif //ME /* ================= 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++; } /* ================ Q2_MoveBrushesToWorld Takes all of the brushes from the current entity and adds them to the world's brush list. Used by func_group and func_areaportal ================ */ void Q2_MoveBrushesToWorld( entity_t *mapent ) { int newbrushes; int worldbrushes; mapbrush_t *temp; int i; // this is pretty gross, because the brushes are expected to be // in linear order for each entity newbrushes = mapent->numbrushes; worldbrushes = entities[0].numbrushes; temp = GetMemory( newbrushes * sizeof( mapbrush_t ) ); memcpy( temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof( mapbrush_t ) ); #if 0 // let them keep their original brush numbers for ( i = 0 ; i < newbrushes ; i++ ) temp[i].entitynum = 0; #endif // make space to move the brushes (overlapped copy) memmove( mapbrushes + worldbrushes + newbrushes, mapbrushes + worldbrushes, sizeof( mapbrush_t ) * ( nummapbrushes - worldbrushes - newbrushes ) ); // copy the new brushes down memcpy( mapbrushes + worldbrushes, temp, sizeof( mapbrush_t ) * newbrushes ); // fix up indexes entities[0].numbrushes += newbrushes; for ( i = 1 ; i < num_entities ; i++ ) entities[i].firstbrush += newbrushes; FreeMemory( temp ); mapent->numbrushes = 0; } /* ================ Q2_ParseMapEntity ================ */ qboolean Q2_ParseMapEntity( script_t *script ) { entity_t *mapent; epair_t *e; side_t *s; int i, j; int startbrush, startsides; vec_t newdist; mapbrush_t *b; token_t token; if ( !PS_ReadToken( script, &token ) ) { return false; } if ( strcmp( token.string, "{" ) ) { 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 ( !PS_ReadToken( script, &token ) ) { Error( "ParseEntity: EOF without closing brace" ); } //end if if ( !strcmp( token.string, "}" ) ) { break; } if ( !strcmp( token.string, "{" ) ) { Q2_ParseBrush( script, mapent ); } //end if else { PS_UnreadLastToken( script ); e = ParseEpair( script ); e->next = mapent->epairs; mapent->epairs = e; } //end else } 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" ) ) ) { Q2_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 ); Q2_MoveBrushesToWorld( mapent ); return true; } return true; } //=================================================================== /* ================ LoadMapFile ================ */ void Q2_LoadMapFile( char *filename ) { int i; script_t *script; Log_Print( "-- Q2_LoadMapFile --\n" ); #ifdef ME //loaded map type loadedmaptype = MAPTYPE_QUAKE2; //reset the map loading ResetMapLoading(); #endif //ME script = LoadScriptFile( filename ); if ( !script ) { Log_Print( "couldn't open %s\n", filename ); return; } //end if //white spaces and escape characters inside a string are not allowed SetScriptFlags( script, SCFL_NOSTRINGWHITESPACES | SCFL_NOSTRINGESCAPECHARS | SCFL_PRIMITIVE ); nummapbrushsides = 0; num_entities = 0; while ( Q2_ParseMapEntity( script ) ) { } ClearBounds( map_mins, map_maxs ); for ( i = 0 ; i < entities[0].numbrushes ; i++ ) { if ( mapbrushes[i].mins[0] > 4096 ) { continue; // no valid points } AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs ); AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs ); } //end for PrintMapInfo(); //free the script FreeScript( script ); // TestExpandBrushes (); // Q2_CreateMapTexinfo(); } //end of the function Q2_LoadMapFile #ifdef ME //Begin MAP loading from BSP file //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q2_SetLeafBrushesModelNumbers( int leafnum, int modelnum ) { int i, brushnum; dleaf_t *leaf; leaf = &dleafs[leafnum]; for ( i = 0; i < leaf->numleafbrushes; i++ ) { brushnum = dleafbrushes[leaf->firstleafbrush + i]; brushmodelnumbers[brushnum] = modelnum; dbrushleafnums[brushnum] = leafnum; } //end for } //end of the function Q2_SetLeafBrushesModelNumbers //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q2_InitNodeStack( void ) { nodestackptr = nodestack; nodestacksize = 0; } //end of the function Q2_InitNodeStack //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q2_PushNodeStack( int num ) { *nodestackptr = num; nodestackptr++; nodestacksize++; // if ( nodestackptr >= &nodestack[NODESTACKSIZE] ) { Error( "Q2_PushNodeStack: stack overflow\n" ); } //end if } //end of the function Q2_PushNodeStack //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int Q2_PopNodeStack( void ) { //if the stack is empty if ( nodestackptr <= nodestack ) { return -1; } //decrease stack pointer nodestackptr--; nodestacksize--; //return the top value from the stack return *nodestackptr; } //end of the function Q2_PopNodeStack //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q2_SetBrushModelNumbers( entity_t *mapent ) { int n, pn; int leafnum; // Q2_InitNodeStack(); //head node (root) of the bsp tree n = dmodels[mapent->modelnum].headnode; pn = 0; do { //if we are in a leaf (negative node number) if ( n < 0 ) { //number of the leaf leafnum = ( -n ) - 1; //set the brush numbers Q2_SetLeafBrushesModelNumbers( leafnum, mapent->modelnum ); //walk back into the tree to find a second child to continue with for ( pn = Q2_PopNodeStack(); pn >= 0; n = pn, pn = Q2_PopNodeStack() ) { //if we took the first child at the parent node if ( dnodes[pn].children[0] == n ) { break; } } //end for //if the stack wasn't empty (if not processed whole tree) if ( pn >= 0 ) { //push the parent node again Q2_PushNodeStack( pn ); //we proceed with the second child of the parent node n = dnodes[pn].children[1]; } //end if } //end if else { //push the current node onto the stack Q2_PushNodeStack( n ); //walk forward into the tree to the first child n = dnodes[n].children[0]; } //end else } while ( pn >= 0 ); } //end of the function Q2_SetBrushModelNumbers //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q2_BSPBrushToMapBrush( dbrush_t *bspbrush, entity_t *mapent ) { mapbrush_t *b; int i, k, n; side_t *side, *s2; int planenum; dbrushside_t *bspbrushside; dplane_t *bspplane; if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) { Error( "nummapbrushes >= MAX_MAPFILE_BRUSHES" ); } b = &mapbrushes[nummapbrushes]; b->original_sides = &brushsides[nummapbrushsides]; b->entitynum = mapent - entities; b->brushnum = nummapbrushes - mapent->firstbrush; b->leafnum = dbrushleafnums[bspbrush - dbrushes]; for ( n = 0; n < bspbrush->numsides; n++ ) { //pointer to the bsp brush side bspbrushside = &dbrushsides[bspbrush->firstside + n]; if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) { Error( "MAX_MAPFILE_BRUSHSIDES" ); } //end if //pointer to the map brush side side = &brushsides[nummapbrushsides]; //if the BSP brush side is textured if ( brushsidetextured[bspbrush->firstside + n] ) { side->flags |= SFL_TEXTURED; } else { side->flags &= ~SFL_TEXTURED;} //ME: can get side contents and surf directly from BSP file side->contents = bspbrush->contents; //if the texinfo is TEXINFO_NODE if ( bspbrushside->texinfo < 0 ) { side->surf = 0; } else { side->surf = texinfo[bspbrushside->texinfo].flags;} // 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; } //ME: get a plane for this side bspplane = &dplanes[bspbrushside->planenum]; planenum = FindFloatPlane( bspplane->normal, bspplane->dist ); // // see if the plane has been used already // //ME: this really shouldn't happen!!! //ME: otherwise the bsp file is corrupted?? //ME: still it seems to happen, maybe Johny Boy's //ME: brush bevel adding is crappy ? for ( k = 0; k < b->numsides; k++ ) { s2 = b->original_sides + k; // if (DotProduct (mapplanes[s2->planenum].normal, mapplanes[planenum].normal) > 0.999 // && fabs(mapplanes[s2->planenum].dist - mapplanes[planenum].dist) < 0.01 ) 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 // //ME: reset pointer to side, why? hell I dunno (pointer is set above already) side = b->original_sides + b->numsides; //ME: store the plane number side->planenum = planenum; //ME: texinfo is already stored when bsp is loaded //NOTE: check for TEXINFO_NODE, otherwise crash in Q2_BrushContents if ( bspbrushside->texinfo < 0 ) { side->texinfo = 0; } else { side->texinfo = bspbrushside->texinfo;} // save the td off in case there is an origin brush and we // have to recalculate the texinfo // ME: don't need to recalculate because it's already done // (for non-world entities) in the BSP file // side_brushtextures[nummapbrushsides] = td; nummapbrushsides++; b->numsides++; } //end for // get the content for the entire brush b->contents = bspbrush->contents; Q2_BrushContents( b ); if ( BrushExists( b ) ) { c_squattbrushes++; b->numsides = 0; return; } //end if //if we're creating AAS if ( create_aas ) { //create the AAS brushes from this brush, don't add brush bevels AAS_CreateMapBrushes( b, mapent, false ); return; } //end if // allow detail brushes to be removed if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) { b->numsides = 0; return; } //end if // allow water brushes to be removed if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) { b->numsides = 0; return; } //end if // create windings for sides and bounds for brush MakeBrushWindings( b ); //mark brushes without winding or with a tiny window as bevels MarkBrushBevels( 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; } //end for // // 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 // //ME: not needed because the entities in the BSP file already // have an origin set // 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; // } //ME: the bsp brushes already have bevels, so we won't try to // add them again (especially since Johny Boy's bevel adding might // be crappy) // AddBrushBevels(b); nummapbrushes++; mapent->numbrushes++; } //end of the function Q2_BSPBrushToMapBrush //=========================================================================== //=========================================================================== void Q2_ParseBSPBrushes( entity_t *mapent ) { int i; //give all the brushes that belong to this entity the number of the //BSP model used by this entity Q2_SetBrushModelNumbers( mapent ); //now parse all the brushes with the correct mapent->modelnum for ( i = 0; i < numbrushes; i++ ) { if ( brushmodelnumbers[i] == mapent->modelnum ) { Q2_BSPBrushToMapBrush( &dbrushes[i], mapent ); } //end if } //end for } //end of the function Q2_ParseBSPBrushes //=========================================================================== //=========================================================================== qboolean Q2_ParseBSPEntity( int entnum ) { entity_t *mapent; char *model; int startbrush, startsides; startbrush = nummapbrushes; startsides = nummapbrushsides; mapent = &entities[entnum]; //num_entities]; mapent->firstbrush = nummapbrushes; mapent->numbrushes = 0; mapent->modelnum = -1; //-1 = no model model = ValueForKey( mapent, "model" ); if ( model && strlen( model ) ) { if ( *model != '*' ) { Error( "Q2_ParseBSPEntity: model number without leading *" ); } //end if //get the model number of this entity (skip the leading *) mapent->modelnum = atoi( &model[1] ); } //end if GetVectorForKey( mapent, "origin", mapent->origin ); //if this is the world entity it has model number zero //the world entity has no model key if ( !strcmp( "worldspawn", ValueForKey( mapent, "classname" ) ) ) { mapent->modelnum = 0; } //end if //if the map entity has a BSP model (a modelnum of -1 is used for //entities that aren't using a BSP model) if ( mapent->modelnum >= 0 ) { //parse the bsp brushes Q2_ParseBSPBrushes( mapent ); } //end if // //the origin of the entity is already taken into account // //func_group entities can't be in the bsp file // //check out the func_areaportal entities if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) { c_areaportals++; mapent->areaportalnum = c_areaportals; return true; } //end if return true; } //end of the function Q2_ParseBSPEntity //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void Q2_LoadMapFromBSP( char *filename, int offset, int length ) { int i; Log_Print( "-- Q2_LoadMapFromBSP --\n" ); //loaded map type loadedmaptype = MAPTYPE_QUAKE2; Log_Print( "Loading map from %s...\n", filename ); //load the bsp file Q2_LoadBSPFile( filename, offset, length ); //create an index from bsp planes to map planes //DPlanes2MapPlanes(); //clear brush model numbers for ( i = 0; i < MAX_MAPFILE_BRUSHES; i++ ) brushmodelnumbers[i] = -1; nummapbrushsides = 0; num_entities = 0; Q2_ParseEntities(); // for ( i = 0; i < num_entities; i++ ) { Q2_ParseBSPEntity( i ); } //end for //get the map mins and maxs from the world model ClearBounds( map_mins, map_maxs ); for ( i = 0; i < entities[0].numbrushes; i++ ) { if ( mapbrushes[i].mins[0] > 4096 ) { continue; //no valid points } AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs ); AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs ); } //end for PrintMapInfo(); // Q2_CreateMapTexinfo(); } //end of the function Q2_LoadMapFromBSP void Q2_ResetMapLoading( void ) { //reset for map loading from bsp memset( nodestack, 0, NODESTACKSIZE * sizeof( int ) ); nodestackptr = NULL; nodestacksize = 0; memset( brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof( int ) ); } //end of the function Q2_ResetMapLoading //End MAP loading from BSP file #endif //ME //==================================================================== /* ================ TestExpandBrushes Expands all the brush planes and saves a new map out ================ */ void TestExpandBrushes( void ) { FILE *f; side_t *s; int i, j, bn; winding_t *w; char *name = "expanded.map"; mapbrush_t *brush; vec_t dist; Log_Print( "writing %s\n", name ); f = fopen( name, "wb" ); if ( !f ) { Error( "Can't write %s\n", name ); } fprintf( f, "{\n\"classname\" \"worldspawn\"\n" ); for ( bn = 0 ; bn < nummapbrushes ; bn++ ) { brush = &mapbrushes[bn]; fprintf( f, "{\n" ); for ( i = 0 ; i < brush->numsides ; i++ ) { s = brush->original_sides + i; dist = mapplanes[s->planenum].dist; for ( j = 0 ; j < 3 ; j++ ) dist += fabs( 16 * mapplanes[s->planenum].normal[j] ); w = BaseWindingForPlane( mapplanes[s->planenum].normal, dist ); fprintf( f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] ); fprintf( f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] ); fprintf( f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] ); fprintf( f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture ); FreeWinding( w ); } fprintf( f, "}\n" ); } fprintf( f, "}\n" ); fclose( f ); Error( "can't proceed after expanding brushes" ); } //end of the function TestExpandBrushes