/* ================ Host_AbortCurrentFrame aborts the current host frame and goes on with the next one ================ */ void Host_AbortCurrentFrame( void ) { if( host.framecount == 0 ) // abort frame was not set up Sys_Break("Could not abort current frame"); else longjmp( host.abortframe, 1 ); }
/* ================ Host_EndGame ================ */ void Host_EndGame( const char *message, ... ) { va_list argptr; static char string[MAX_SYSPATH]; va_start( argptr, message ); Q_vsprintf( string, message, argptr ); va_end( argptr ); MsgDev( D_INFO, "Host_EndGame: %s\n", string ); if( SV_Active()) { Q_snprintf( host.finalmsg, sizeof( host.finalmsg ), "Host_EndGame: %s", string ); SV_Shutdown( false ); return; } if( host.type == HOST_DEDICATED ) Sys_Break( "Host_EndGame: %s\n", string ); // dedicated servers exit SV_Shutdown( false ); CL_Disconnect(); // recreate world if needs CL_ClearEdicts (); // release all models Mod_ClearAll( true ); Host_AbortCurrentFrame (); }
/* ================= LoadMapFile loads a map file into a list of entities ================= */ void LoadMapFile( const char *filename, bool onlyLights ) { brush_t *b; int oldNumEntities, numMapBrushes; MsgDev( D_NOTE, "--- LoadMapFile ---\n" ); Msg( "Loading %s\n", filename ); mapfile = Com_OpenScript( filename, NULL, 0 ); if( !mapfile ) Sys_Break( "can't loading map file %s\n", filename ); if( onlyLights ) oldNumEntities = numEntities; else numEntities = 0; c_detail = 0; numMapDrawSurfs = 0; g_bBrushPrimit = BRUSH_UNKNOWN; // allocate a very large temporary brush for building the brushes as they are loaded buildBrush = AllocBrush( MAX_BUILD_SIDES ); while( ParseMapEntity( onlyLights )); Com_CloseScript( mapfile ); if( onlyLights ) { MsgDev( D_INFO, "%9d light entities\n", numEntities - oldNumEntities ); } else { ClearBounds( mapMins, mapMaxs ); for( b = entities[0].brushes; b; b = b->next ) { AddPointToBounds( b->mins, mapMins, mapMaxs ); AddPointToBounds( b->maxs, mapMins, mapMaxs ); } VectorSubtract( mapMaxs, mapMins, mapSize ); numMapBrushes = CountBrushList( entities[0].brushes ); if(( float )c_detail / (float) numMapBrushes < 0.10f && numMapBrushes > 500 ) MsgDev( D_WARN, "Over 90 percent structural map detected. Compile time may be adversely affected.\n" ); MsgDev( D_NOTE, "%9d total world brushes\n", numMapBrushes ); MsgDev( D_NOTE, "%9d detail brushes\n", c_detail ); MsgDev( D_NOTE, "%9d patches\n", numMapPatches ); MsgDev( D_NOTE, "%9d boxbevels\n", c_boxbevels ); MsgDev( D_NOTE, "%9d edgebevels\n", c_edgebevels ); MsgDev( D_NOTE, "%9d entities\n", numEntities ); MsgDev( D_NOTE, "%9d planes\n", nummapplanes ); MsgDev( D_NOTE, "%9d areaportals\n", c_areaportals ); MsgDev( D_INFO, "World size: %5.0f, %5.0f, %5.0f\n", mapSize[0], mapSize[1], mapSize[2] ); // write bogus map if( fakemap ) WriteBSPBrushMap( "fakemap.map", entities[0].brushes ); } }
/* ================= ParseRawBrush parses the sides into buildBrush->sides[], nothing else. no validation, back plane removal, etc. ================= */ static void ParseRawBrush( bool onlyLights ) { side_t *side; float planePoints[3][3]; int planenum; char name[MAX_SHADERPATH]; char shader[MAX_SHADERPATH]; shaderInfo_t *si; token_t token; vects_t vects; int flags; buildBrush->numsides = 0; buildBrush->detail = false; if( g_bBrushPrimit == BRUSH_RADIANT ) Com_CheckToken( mapfile, "{" ); while( 1 ) { if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES|SC_COMMENT_SEMICOLON, &token )) break; if( !com.stricmp( token.string, "}" )) break; if( g_bBrushPrimit == BRUSH_RADIANT ) { while( 1 ) { if( com.strcmp( token.string, "(" )) Com_ReadToken( mapfile, 0, &token ); else break; Com_ReadToken( mapfile, SC_ALLOW_NEWLINES, &token ); } } Com_SaveToken( mapfile, &token ); if( buildBrush->numsides >= MAX_BUILD_SIDES ) Sys_Break( "Entity %i, Brush %i MAX_BUILD_SIDES\n", buildBrush->entityNum, buildBrush->brushNum ); side = &buildBrush->sides[ buildBrush->numsides ]; Mem_Set( side, 0, sizeof( *side )); buildBrush->numsides++; // read the three point plane definition Com_Parse1DMatrix( mapfile, 3, planePoints[0] ); Com_Parse1DMatrix( mapfile, 3, planePoints[1] ); Com_Parse1DMatrix( mapfile, 3, planePoints[2] ); // read the texture matrix if( g_bBrushPrimit == BRUSH_RADIANT ) Com_Parse2DMatrix( mapfile, 2, 3, (float *)side->texMat ); // read the texturedef or shadername Com_ReadToken( mapfile, SC_ALLOW_PATHNAMES|SC_PARSE_GENERIC, &token ); com.strncpy( name, token.string, sizeof( name )); if( g_bBrushPrimit == BRUSH_WORLDCRAFT_22 || g_bBrushPrimit == BRUSH_GEARCRAFT_40 ) // Worldcraft 2.2+ { // texture U axis Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "[" )) Sys_Break( "missing '[' in texturedef (U)\n" ); Com_ReadFloat( mapfile, false, &vects.hammer.UAxis[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.UAxis[1] ); Com_ReadFloat( mapfile, false, &vects.hammer.UAxis[2] ); Com_ReadFloat( mapfile, false, &vects.hammer.shift[0] ); Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "]" )) Sys_Break( "missing ']' in texturedef (U)\n" ); // texture V axis Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "[" )) Sys_Break( "missing '[' in texturedef (V)\n" ); Com_ReadFloat( mapfile, false, &vects.hammer.VAxis[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.VAxis[1] ); Com_ReadFloat( mapfile, false, &vects.hammer.VAxis[2] ); Com_ReadFloat( mapfile, false, &vects.hammer.shift[1] ); Com_ReadToken( mapfile, 0, &token ); if( com.strcmp( token.string, "]" )) Sys_Break( "missing ']' in texturedef (V)\n"); // texture rotation is implicit in U/V axes. Com_ReadToken( mapfile, 0, &token ); vects.hammer.rotate = 0; // texure scale Com_ReadFloat( mapfile, false, &vects.hammer.scale[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.scale[1] ); if( g_bBrushPrimit == BRUSH_GEARCRAFT_40 ) { Com_ReadLong( mapfile, false, &flags ); // read gearcraft flags Com_SkipRestOfLine( mapfile ); // gearcraft lightmap scale and rotate if( flags & GEARBOX_DETAIL ) side->compileFlags |= C_DETAIL; } } else if( g_bBrushPrimit == BRUSH_WORLDCRAFT_21 || g_bBrushPrimit == BRUSH_QUARK ) { // worldcraft 2.1-, old Radiant, QuArK Com_ReadFloat( mapfile, false, &vects.hammer.shift[0] ); Com_ReadFloat( mapfile, false, &vects.hammer.shift[1] ); Com_ReadFloat( mapfile, false, &vects.hammer.rotate ); Com_ReadFloat( mapfile, false, &vects.hammer.scale[0] ); Com_ReadFloat( mapfile, SC_COMMENT_SEMICOLON, &vects.hammer.scale[1] ); } // set default flags and values com.sprintf( shader, "textures/%s", name ); if( onlyLights ) si = &shaderInfo[0]; else si = ShaderInfoForShader( shader ); side->shaderInfo = si; side->surfaceFlags = si->surfaceFlags; side->contentFlags = si->contentFlags; side->compileFlags = si->compileFlags; side->value = si->value; // bias texture shift for non-radiant sides if( g_bBrushPrimit != BRUSH_RADIANT && si->globalTexture == false ) { vects.hammer.shift[0] -= (floor( vects.hammer.shift[0] / si->shaderWidth ) * si->shaderWidth); vects.hammer.shift[1] -= (floor( vects.hammer.shift[1] / si->shaderHeight ) * si->shaderHeight); } // historically, there are 3 integer values at the end of a brushside line in a .map file. // in quake 3, the only thing that mattered was the first of these three values, which // was previously the content flags. and only then did a single bit matter, the detail // bit. because every game has its own special flags for specifying detail, the // traditionally game-specified BASECONT_DETAIL flag was overridden for Q3Map 2.3.0 // by C_DETAIL, defined in q3map2.h. the value is exactly as it was before, but // is stored in compileFlags, as opposed to contentFlags, for multiple-game // portability. :sigh: if( g_bBrushPrimit != BRUSH_QUARK && Com_ReadToken( mapfile, SC_COMMENT_SEMICOLON, &token )) { // overwrite shader values directly from .map file Com_SaveToken( mapfile, &token ); Com_ReadLong( mapfile, false, &flags ); Com_ReadLong( mapfile, false, NULL ); Com_ReadLong( mapfile, false, NULL ); if( flags & C_DETAIL ) side->compileFlags |= C_DETAIL; } if( mapfile->TXcommand == '1' || mapfile->TXcommand == '2' ) { // we are QuArK mode and need to translate some numbers to align textures its way // from QuArK, the texture vectors are given directly from the three points vec3_t texMat[2]; float dot22, dot23, dot33, mdet, aa, bb, dd; int j, k; g_bBrushPrimit = BRUSH_QUARK; // we can detect it only here k = mapfile->TXcommand - '0'; for( j = 0; j < 3; j++ ) texMat[1][j] = (planePoints[k][j] - planePoints[0][j]) * (0.0078125f); // QuArK magic value k = 3 - k; for( j = 0; j < 3; j++ ) texMat[0][j] = (planePoints[k][j] - planePoints[0][j]) * (0.0078125f); // QuArK magic value dot22 = DotProduct( texMat[0], texMat[0] ); dot23 = DotProduct( texMat[0], texMat[1] ); dot33 = DotProduct( texMat[1], texMat[1] ); mdet = dot22 * dot33 - dot23 * dot23; if( mdet < 1E-6 && mdet > -1E-6 ) { aa = bb = dd = 0; MsgDev( D_WARN, "Entity %i, Brush %i: degenerate QuArK-style texture: \n", buildBrush->entityNum, buildBrush->brushNum ); } else { mdet = 1.0 / mdet; aa = dot33 * mdet; bb = -dot23 * mdet; dd = dot22 * mdet; } for( j = 0; j < 3; j++ ) { vects.quark.vecs[0][j] = aa * texMat[0][j] + bb * texMat[1][j]; vects.quark.vecs[1][j] = -(bb * texMat[0][j] + dd * texMat[1][j]); } vects.quark.vecs[0][3] = -DotProduct( vects.quark.vecs[0], planePoints[0] ); vects.quark.vecs[1][3] = -DotProduct( vects.quark.vecs[1], planePoints[0] ); } // find the plane number planenum = MapPlaneFromPoints( planePoints ); if( planenum == -1 ) { MsgDev( D_ERROR, "Entity %i, Brush %i: plane with no normal\n", buildBrush->entityNum, buildBrush->brushNum ); continue; } side->planenum = planenum; if( g_bBrushPrimit == BRUSH_QUARK ) { // QuArK format completely matched with internal // FIXME: don't calculate vecs, use QuArK texMat instead ? Mem_Copy( side->vecs, vects.quark.vecs, sizeof( side->vecs )); } else if( g_bBrushPrimit != BRUSH_RADIANT ) { vec3_t vecs[2]; float ang, sinv, cosv, ns, nt; int i, j, sv, tv; if( g_bBrushPrimit == BRUSH_WORLDCRAFT_21 ) TextureAxisFromPlane( &mapplanes[planenum], vecs[0], vecs[1] ); if( !vects.hammer.scale[0] ) vects.hammer.scale[0] = 1.0f; if( !vects.hammer.scale[1] ) vects.hammer.scale[1] = 1.0f; if( g_bBrushPrimit == BRUSH_WORLDCRAFT_21 ) { // rotate axis if( vects.hammer.rotate == 0 ) { sinv = 0; cosv = 1; } else if( vects.hammer.rotate == 90 ) { sinv = 1; cosv = 0; } else if( vects.hammer.rotate == 180 ) { sinv = 0; cosv = -1; } else if( vects.hammer.rotate == 270 ) { sinv = -1; cosv = 0; } else { ang = vects.hammer.rotate / 180 * M_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++ ) side->vecs[i][j] = vecs[i][j] / vects.hammer.scale[i]; } else if( g_bBrushPrimit == BRUSH_WORLDCRAFT_22 || g_bBrushPrimit == BRUSH_GEARCRAFT_40 ) { float scale; scale = 1.0f / vects.hammer.scale[0]; VectorScale( vects.hammer.UAxis, scale, side->vecs[0] ); scale = 1.0f / vects.hammer.scale[1]; VectorScale( vects.hammer.VAxis, scale, side->vecs[1] ); } // add shifts side->vecs[0][3] = vects.hammer.shift[0]; side->vecs[1][3] = vects.hammer.shift[1]; } } if( g_bBrushPrimit == BRUSH_RADIANT ) { Com_SaveToken( mapfile, &token ); Com_CheckToken( mapfile, "}" ); Com_CheckToken( mapfile, "}" ); } }
/* ================= AddBrushBevels adds any additional planes necessary to allow the brush being built to be expanded against axial bounding boxes 2003-01-20: added mr.Elusive fixes ================= */ void AddBrushBevels( void ) { int axis, dir; int i, j, k, l, order = 0; side_t sidetemp; side_t *s, *s2; winding_t *w, *w2; vec3_t normal; float dist; vec3_t vec, vec2; float d, minBack; // add the axial planes for( axis = 0; axis < 3; axis++ ) { for( dir = -1; dir <= 1; dir += 2, order++ ) { // see if the plane is allready present for( i = 0, s = buildBrush->sides; i < buildBrush->numsides; i++, s++ ) { if( mapplanes[s->planenum].normal[axis] == dir ) break; } if( i == buildBrush->numsides ) { // add a new side if( buildBrush->numsides == MAX_BUILD_SIDES ) Sys_Break( "Entity %i, Brush %i MAX_BUILD_SIDES\n", buildBrush->entityNum, buildBrush->brushNum ); Mem_Set( s, 0, sizeof( *s )); buildBrush->numsides++; VectorClear (normal); normal[axis] = dir; if( dir == 1 ) { // adding bevel plane snapping for fewer bsp planes if( bevelSnap > 0 ) dist = floor( buildBrush->maxs[axis] / bevelSnap ) * bevelSnap; else dist = buildBrush->maxs[axis]; } else { // adding bevel plane snapping for fewer bsp planes if( bevelSnap > 0 ) dist = -ceil( buildBrush->mins[axis] / bevelSnap ) * bevelSnap; else dist = -buildBrush->mins[axis]; } s->planenum = FindFloatPlane( normal, dist, 0, NULL ); s->contentFlags = buildBrush->sides[0].contentFlags; s->bevel = true; c_boxbevels++; } // if the plane is not in it canonical order, swap it if( i != order ) { sidetemp = buildBrush->sides[order]; buildBrush->sides[order] = buildBrush->sides[i]; buildBrush->sides[i] = sidetemp; } } } // add the edge bevels if( buildBrush->numsides == 6 ) return; // pure axial // test the non-axial plane edges for( i = 6; i < buildBrush->numsides; i++ ) { s = buildBrush->sides + i; w = s->winding; if( !w ) continue; for( j = 0; j < w->numpoints; j++ ) { k = (j+1)%w->numpoints; VectorSubtract( w->p[j], w->p[k], vec ); if( VectorNormalizeLength( vec ) < 0.5f ) continue; SnapNormal( vec ); for( k = 0; k < 3; k++ ) { if( vec[k] == -1.0f || vec[k] == 1.0f || (vec[k] == 0.0f && vec[(k+1)%3] == 0.0f)) break; // axial } if( k != 3 ) continue; // only test non-axial edges // try the six possible slanted axials from this edge for( axis = 0; axis < 3; axis++ ) { for( dir = -1; dir <= 1; dir += 2 ) { // construct a plane VectorClear( vec2 ); vec2[axis] = dir; CrossProduct( vec, vec2, normal ); if( VectorNormalizeLength( normal ) < 0.5f ) continue; dist = DotProduct( w->p[j], normal ); // if all the points on all the sides are // behind this plane, it is a proper edge bevel for( k = 0; k < buildBrush->numsides; k++ ) { // if this plane has allready been used, skip it if( PlaneEqual( &mapplanes[buildBrush->sides[k].planenum], normal, dist )) break; w2 = buildBrush->sides[k].winding; if( !w2 ) continue; minBack = 0.0f; for( l = 0; l < w2->numpoints; l++ ) { d = DotProduct( w2->p[l], normal ) - dist; if( d > 0.1f ) break; // point in front if( d < minBack ) minBack = d; } // if some point was at the front if( l != w2->numpoints ) break; // if no points at the back then the winding is on the bevel plane if( minBack > -0.1f ) break; } if( k != buildBrush->numsides ) continue; // wasn't part of the outer hull // add this plane if( buildBrush->numsides == MAX_BUILD_SIDES ) Sys_Break( "Entity %i, Brush %i MAX_BUILD_SIDES\n", buildBrush->entityNum, buildBrush->brushNum ); s2 = &buildBrush->sides[buildBrush->numsides]; buildBrush->numsides++; Mem_Set( s2, 0, sizeof( *s2 ) ); s2->planenum = FindFloatPlane( normal, dist, 1, &w->p[j] ); s2->contentFlags = buildBrush->sides[0].contentFlags; s2->bevel = true; c_edgebevels++; } } } } }
/* ================= ParseMapEntity parses a single entity out of a map file ================= */ static bool ParseMapEntity( bool onlyLights ) { epair_t *ep; token_t token; const char *classname, *value; float lightmapScale; char shader[ MAX_SHADERPATH ]; shaderInfo_t *celShader = NULL; brush_t *brush; parseMesh_t *patch; bool funcGroup; int castShadows, recvShadows; static bool map_type = false; if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES|SC_COMMENT_SEMICOLON, &token )) return false; // end of .map file if( com.stricmp( token.string, "{" )) Sys_Break( "ParseEntity: found %s instead {\n", token.string ); if( numEntities >= MAX_MAP_ENTITIES ) Sys_Break( "MAX_MAP_ENTITIES limit exceeded\n" ); entitySourceBrushes = 0; mapEnt = &entities[numEntities]; numEntities++; memset( mapEnt, 0, sizeof( *mapEnt )); mapEnt->mapEntityNum = numMapEntities; numMapEntities++; while( 1 ) { if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES|SC_COMMENT_SEMICOLON, &token )) Sys_Break( "ParseEntity: EOF without closing brace\n" ); if( !com.stricmp( token.string, "}" )) break; if( !com.stricmp( token.string, "{" )) { // parse a brush or patch if( !Com_ReadToken( mapfile, SC_ALLOW_NEWLINES, &token )) break; if( !com.stricmp( token.string, "patchDef2" )) { numMapPatches++; ParsePatch( onlyLights ); g_bBrushPrimit = BRUSH_RADIANT; } else if( !com.stricmp( token.string, "terrainDef" )) { MsgDev( D_WARN, "Terrain entity parsing not supported in this build.\n" ); Com_SkipBracedSection( mapfile, 0 ); g_bBrushPrimit = BRUSH_RADIANT; } else if( !com.stricmp( token.string, "brushDef" )) { // parse brush primitive g_bBrushPrimit = BRUSH_RADIANT; ParseBrush( onlyLights ); } else { if( g_bBrushPrimit == BRUSH_RADIANT ) Sys_Break( "mixed brush primitive with another format\n" ); if( g_bBrushPrimit == BRUSH_UNKNOWN ) g_bBrushPrimit = BRUSH_WORLDCRAFT_21; // QuArK or WorldCraft map (unknown at this point) Com_SaveToken( mapfile, &token ); ParseBrush( onlyLights ); } entitySourceBrushes++; } else { // parse a key / value pair ep = ParseEpair( mapfile, &token ); if( !com.strcmp( ep->key, "mapversion" )) { if( com.atoi( ep->value ) == VALVE_FORMAT ) { Msg( "Valve Format Map detected\n" ); g_bBrushPrimit = BRUSH_WORLDCRAFT_22; } else if( com.atoi( ep->value ) == GEARBOX_FORMAT ) { Msg( "Gearcraft Format Map detected\n" ); g_bBrushPrimit = BRUSH_GEARCRAFT_40; } else g_bBrushPrimit = BRUSH_WORLDCRAFT_21; } if( ep->key[0] != '\0' && ep->value[0] != '\0' ) { ep->next = mapEnt->epairs; mapEnt->epairs = ep; } } } if( !map_type && g_bBrushPrimit != BRUSH_UNKNOWN ) { MAPTYPE(); map_type = true; } classname = ValueForKey( mapEnt, "classname" ); if( onlyLights && com.strnicmp( classname, "light", 5 )) { numEntities--; return true; } if( !com.stricmp( "func_group", classname )) funcGroup = true; else funcGroup = false; // worldspawn (and func_groups) default to cast/recv shadows in worldspawn group if( funcGroup || mapEnt->mapEntityNum == 0 ) { castShadows = WORLDSPAWN_CAST_SHADOWS; recvShadows = WORLDSPAWN_RECV_SHADOWS; } else // other entities don't cast any shadows, but recv worldspawn shadows { castShadows = ENTITY_CAST_SHADOWS; recvShadows = ENTITY_RECV_SHADOWS; } // get explicit shadow flags GetEntityShadowFlags( mapEnt, NULL, &castShadows, &recvShadows ); // get lightmap scaling value for this entity if( com.strcmp( "", ValueForKey( mapEnt, "lightmapscale" )) || com.strcmp( "", ValueForKey( mapEnt, "_lightmapscale" ))) { // get lightmap scale from entity lightmapScale = FloatForKey( mapEnt, "lightmapscale" ); if( lightmapScale <= 0.0f ) lightmapScale = FloatForKey( mapEnt, "_lightmapscale" ); if( lightmapScale > 0.0f ) Msg( "Entity %d (%s) has lightmap scale of %.4f\n", mapEnt->mapEntityNum, classname, lightmapScale ); } else lightmapScale = 0.0f; // get cel shader :) for this entity value = ValueForKey( mapEnt, "_celshader" ); if( value[0] == '\0' ) value = ValueForKey( &entities[0], "_celshader" ); if( value[0] != '\0' ) { com.snprintf( shader, sizeof( shader ), "textures/%s", value ); celShader = ShaderInfoForShader( shader ); Msg( "Entity %d (%s) has cel shader %s\n", mapEnt->mapEntityNum, classname, celShader->shader ); } else celShader = NULL; // attach stuff to everything in the entity for( brush = mapEnt->brushes; brush != NULL; brush = brush->next ) { brush->entityNum = mapEnt->mapEntityNum; brush->castShadows = castShadows; brush->recvShadows = recvShadows; brush->lightmapScale = lightmapScale; brush->celShader = celShader; } for( patch = mapEnt->patches; patch != NULL; patch = patch->next ) { patch->entityNum = mapEnt->mapEntityNum; patch->castShadows = castShadows; patch->recvShadows = recvShadows; patch->lightmapScale = lightmapScale; patch->celShader = celShader; } SetEntityBounds( mapEnt ); // load shader index map (equivalent to old terrain alphamap) LoadEntityIndexMap( mapEnt ); // get entity origin and adjust brushes GetVectorForKey( mapEnt, "origin", mapEnt->origin ); if( mapEnt->origin[0] || mapEnt->origin[1] || mapEnt->origin[2] ) AdjustBrushesForOrigin( mapEnt ); // group_info entities are just for editor grouping if( !com.stricmp( "group_info", classname )) { numEntities--; return true; } // group entities are just for editor convenience, toss all brushes into worldspawn if( funcGroup ) { MoveBrushesToWorld( mapEnt ); numEntities--; return true; } return true; }
/* ================= LoadSurfaceExtraFile reads a surface info file (<map>.srf) ================= */ void LoadSurfaceExtraFile( const char *path ) { char srfPath[MAX_SYSPATH]; surfaceExtra_t *se; int surfaceNum; script_t *script; token_t token; if( path == NULL || path[0] == '\0' ) return; com.strcpy( srfPath, path ); FS_StripExtension( srfPath ); FS_DefaultExtension( srfPath, ".srf" ); Msg( "Loading %s\n", srfPath ); script = (void *)Com_OpenScript( srfPath, NULL, 0 ); if( !script ) Sys_Break( "unable to load %s\n", srfPath ); // q3map is always crashed if this missed while( 1 ) { if( !Com_ReadToken( script, SC_ALLOW_NEWLINES|SC_PARSE_GENERIC, &token )) break; if( !com.stricmp( token.string, "default" )) se = &seDefault; else { surfaceNum = com.atoi( token.string ); if( surfaceNum < 0 || surfaceNum > MAX_MAP_DRAW_SURFS ) Sys_Error( "ReadSurfaceExtraFile(): %s, line %d: bogus surface num %d", srfPath, token.line, surfaceNum ); while( surfaceNum >= numSurfaceExtras ) se = AllocSurfaceExtra(); se = &surfaceExtras[surfaceNum]; } // handle { } section if( !Com_ReadToken( script, SC_ALLOW_NEWLINES|SC_PARSE_GENERIC, &token ) || com.strcmp( token.string, "{" )) Sys_Error( "ReadSurfaceExtraFile(): %s, line %d: { not found\n", srfPath, token.line ); while( 1 ) { if( !Com_ReadToken( script, SC_ALLOW_NEWLINES|SC_PARSE_GENERIC, &token )) break; if( !com.strcmp( token.string, "}" )) break; if( !com.stricmp( token.string, "shader" )) { Com_ReadToken( script, SC_ALLOW_PATHNAMES|SC_PARSE_GENERIC, &token ); se->si = ShaderInfoForShader( token.string ); } else if( !com.stricmp( token.string, "parent" )) { Com_ReadLong( script, false, &se->parentSurfaceNum ); } else if( !com.stricmp( token.string, "entity" )) { Com_ReadLong( script, false, &se->entityNum ); } else if( !com.stricmp( token.string, "castShadows" )) { Com_ReadLong( script, false, &se->castShadows ); } else if( !com.stricmp( token.string, "receiveShadows" )) { Com_ReadLong( script, false, &se->recvShadows ); } else if( !com.stricmp( token.string, "sampleSize" )) { Com_ReadLong( script, false, &se->sampleSize ); } else if( !com.stricmp( token.string, "longestCurve" )) { Com_ReadFloat( script, false, &se->longestCurve ); } else if( !com.stricmp( token.string, "lightmapAxis" )) Com_Parse1DMatrix( script, 3, se->lightmapAxis ); // ignore all other tokens on the line Com_SkipRestOfLine( script ); } } Com_CloseScript( script ); }