/* ================= 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; }
/** * @brief Parsed map entites and brushes * @sa ParseBrush * @param[in] filename The map filename * @param[in] entityString The body of the entity we are parsing */ static bool ParseMapEntity (const char* filename, const char* entityString) { entity_t* mapent; const char* entName; static int worldspawnCount = 0; int notCheckOrFix = !(config.performMapCheck || config.fixMap); if (Q_strnull(GetToken())) return false; if (*parsedToken != '{') Sys_Error("ParseMapEntity: { not found"); if (num_entities == MAX_MAP_ENTITIES) Sys_Error("num_entities == MAX_MAP_ENTITIES (%i)", num_entities); mapent = &entities[num_entities++]; OBJZERO(*mapent); mapent->firstbrush = nummapbrushes; mapent->numbrushes = 0; do { if (Q_strnull(GetToken())) Sys_Error("ParseMapEntity: EOF without closing brace"); if (*parsedToken == '}') break; if (*parsedToken == '{') ParseBrush(mapent, filename); else { epair_t* e = ParseEpair(num_entities); e->next = mapent->epairs; mapent->epairs = e; } } while (true); GetVectorForKey(mapent, "origin", mapent->origin); entName = ValueForKey(mapent, "classname"); /* offset all of the planes and texinfo if needed */ if (IsInlineModelEntity(entName) && VectorNotEmpty(mapent->origin)) AdjustBrushesForOrigin(mapent); if (num_entities == 1 && !Q_streq("worldspawn", entName)) Sys_Error("The first entity must be worldspawn, it is: %s", entName); if (notCheckOrFix && Q_streq("func_group", entName)) { /* group entities are just for editor convenience * toss all brushes into the world entity */ MoveBrushesToWorld(mapent); num_entities--; } else if (IsInlineModelEntity(entName)) { if (mapent->numbrushes == 0 && notCheckOrFix) { Com_Printf("Warning: %s has no brushes assigned (entnum: %i)\n", entName, num_entities); num_entities--; } } else if (Q_streq("worldspawn", entName)) { worldspawnCount++; if (worldspawnCount > 1) Com_Printf("Warning: more than one %s in one map\n", entName); const char* text = entityString; do { const char* token = Com_Parse(&text); if (Q_strnull(token)) break; const char* key = Mem_StrDup(token); token = Com_Parse(&text); if (Q_strnull(token)) break; const char* value = Mem_StrDup(token); epair_t* e = AddEpair(key, value, num_entities); e->next = mapent->epairs; mapent->epairs = e; } while (true); } return true; }