/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition 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. Doom 3 BFG Edition 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 Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition 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 Doom 3 BFG Edition 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. =========================================================================== */ #pragma hdrstop #include "precompiled.h" #include "tr_local.h" /* ================ idRenderWorldLocal::FreeWorld ================ */ void idRenderWorldLocal::FreeWorld() { // this will free all the lightDefs and entityDefs FreeDefs(); // free all the portals and check light/model references for( int i = 0; i < numPortalAreas; i++ ) { portalArea_t* area; portal_t* portal, *nextPortal; area = &portalAreas[i]; for( portal = area->portals; portal; portal = nextPortal ) { nextPortal = portal->next; delete portal->w; R_StaticFree( portal ); } // there shouldn't be any remaining lightRefs or entityRefs if( area->lightRefs.areaNext != &area->lightRefs ) { common->Error( "FreeWorld: unexpected remaining lightRefs" ); } if( area->entityRefs.areaNext != &area->entityRefs ) { common->Error( "FreeWorld: unexpected remaining entityRefs" ); } } if( portalAreas ) { R_StaticFree( portalAreas ); portalAreas = NULL; numPortalAreas = 0; R_StaticFree( areaScreenRect ); areaScreenRect = NULL; } if( doublePortals ) { R_StaticFree( doublePortals ); doublePortals = NULL; numInterAreaPortals = 0; } if( areaNodes ) { R_StaticFree( areaNodes ); areaNodes = NULL; } // free all the inline idRenderModels for( int i = 0; i < localModels.Num(); i++ ) { renderModelManager->RemoveModel( localModels[i] ); delete localModels[i]; } localModels.Clear(); areaReferenceAllocator.Shutdown(); interactionAllocator.Shutdown(); mapName = ""; } /* ================ idRenderWorldLocal::TouchWorldModels ================ */ void idRenderWorldLocal::TouchWorldModels() { for( int i = 0; i < localModels.Num(); i++ ) { renderModelManager->CheckModel( localModels[i]->Name() ); } } /* ================ idRenderWorldLocal::ReadBinaryShadowModel ================ */ idRenderModel* idRenderWorldLocal::ReadBinaryModel( idFile* fileIn ) { idStrStatic< MAX_OSPATH > name; fileIn->ReadString( name ); idRenderModel* model = renderModelManager->AllocModel(); model->InitEmpty( name ); if( model->LoadBinaryModel( fileIn, mapTimeStamp ) ) { return model; } return NULL; } extern idCVar r_binaryLoadRenderModels; /* ================ idRenderWorldLocal::ParseModel ================ */ idRenderModel* idRenderWorldLocal::ParseModel( idLexer* src, const char* mapName, ID_TIME_T mapTimeStamp, idFile* fileOut ) { idToken token; src->ExpectTokenString( "{" ); // parse the name src->ExpectAnyToken( &token ); idRenderModel* model = renderModelManager->AllocModel(); model->InitEmpty( token ); if( fileOut != NULL ) { // write out the type so the binary reader knows what to instantiate fileOut->WriteString( "shadowmodel" ); fileOut->WriteString( token ); } int numSurfaces = src->ParseInt(); if( numSurfaces < 0 ) { src->Error( "R_ParseModel: bad numSurfaces" ); } for( int i = 0; i < numSurfaces; i++ ) { src->ExpectTokenString( "{" ); src->ExpectAnyToken( &token ); modelSurface_t surf; surf.shader = declManager->FindMaterial( token ); ( ( idMaterial* )surf.shader )->AddReference(); srfTriangles_t* tri = R_AllocStaticTriSurf(); surf.geometry = tri; tri->numVerts = src->ParseInt(); tri->numIndexes = src->ParseInt(); // parse the vertices idTempArray verts( tri->numVerts * 8 ); for( int j = 0; j < tri->numVerts; j++ ) { src->Parse1DMatrix( 8, &verts[j * 8] ); } // parse the indices idTempArray indexes( tri->numIndexes ); for( int j = 0; j < tri->numIndexes; j++ ) { indexes[j] = src->ParseInt(); } #if 1 // find the island that each vertex belongs to idTempArray vertIslands( tri->numVerts ); idTempArray trisVisited( tri->numIndexes ); vertIslands.Zero(); trisVisited.Zero(); int numIslands = 0; for( int j = 0; j < tri->numIndexes; j += 3 ) { if( trisVisited[j] ) { continue; } int islandNum = ++numIslands; vertIslands[indexes[j + 0]] = islandNum; vertIslands[indexes[j + 1]] = islandNum; vertIslands[indexes[j + 2]] = islandNum; trisVisited[j] = true; idList queue; queue.Append( j ); for( int n = 0; n < queue.Num(); n++ ) { int t = queue[n]; for( int k = 0; k < tri->numIndexes; k += 3 ) { if( trisVisited[k] ) { continue; } bool connected = indexes[t + 0] == indexes[k + 0] || indexes[t + 0] == indexes[k + 1] || indexes[t + 0] == indexes[k + 2] || indexes[t + 1] == indexes[k + 0] || indexes[t + 1] == indexes[k + 1] || indexes[t + 1] == indexes[k + 2] || indexes[t + 2] == indexes[k + 0] || indexes[t + 2] == indexes[k + 1] || indexes[t + 2] == indexes[k + 2]; if( connected ) { vertIslands[indexes[k + 0]] = islandNum; vertIslands[indexes[k + 1]] = islandNum; vertIslands[indexes[k + 2]] = islandNum; trisVisited[k] = true; queue.Append( k ); } } } } // center the texture coordinates for each island for maximum 16-bit precision for( int j = 1; j <= numIslands; j++ ) { float minS = idMath::INFINITY; float minT = idMath::INFINITY; float maxS = -idMath::INFINITY; float maxT = -idMath::INFINITY; for( int k = 0; k < tri->numVerts; k++ ) { if( vertIslands[k] == j ) { minS = Min( minS, verts[k * 8 + 3] ); maxS = Max( maxS, verts[k * 8 + 3] ); minT = Min( minT, verts[k * 8 + 4] ); maxT = Max( maxT, verts[k * 8 + 4] ); } } const float averageS = idMath::Ftoi( ( minS + maxS ) * 0.5f ); const float averageT = idMath::Ftoi( ( minT + maxT ) * 0.5f ); for( int k = 0; k < tri->numVerts; k++ ) { if( vertIslands[k] == j ) { verts[k * 8 + 3] -= averageS; verts[k * 8 + 4] -= averageT; } } } #endif R_AllocStaticTriSurfVerts( tri, tri->numVerts ); for( int j = 0; j < tri->numVerts; j++ ) { tri->verts[j].xyz[0] = verts[j * 8 + 0]; tri->verts[j].xyz[1] = verts[j * 8 + 1]; tri->verts[j].xyz[2] = verts[j * 8 + 2]; tri->verts[j].SetTexCoord( verts[j * 8 + 3], verts[j * 8 + 4] ); tri->verts[j].SetNormal( verts[j * 8 + 5], verts[j * 8 + 6], verts[j * 8 + 7] ); } R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for( int j = 0; j < tri->numIndexes; j++ ) { tri->indexes[j] = indexes[j]; } src->ExpectTokenString( "}" ); // add the completed surface to the model model->AddSurface( surf ); } src->ExpectTokenString( "}" ); model->FinishSurfaces(); if( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) { model->WriteBinaryModel( fileOut, &mapTimeStamp ); } return model; } /* ================ idRenderWorldLocal::ReadBinaryShadowModel ================ */ idRenderModel* idRenderWorldLocal::ReadBinaryShadowModel( idFile* fileIn ) { idStrStatic< MAX_OSPATH > name; fileIn->ReadString( name ); idRenderModel* model = renderModelManager->AllocModel(); model->InitEmpty( name ); if( model->LoadBinaryModel( fileIn, mapTimeStamp ) ) { return model; } return NULL; } /* ================ idRenderWorldLocal::ParseShadowModel ================ */ idRenderModel* idRenderWorldLocal::ParseShadowModel( idLexer* src, idFile* fileOut ) { idToken token; src->ExpectTokenString( "{" ); // parse the name src->ExpectAnyToken( &token ); idRenderModel* model = renderModelManager->AllocModel(); model->InitEmpty( token ); if( fileOut != NULL ) { // write out the type so the binary reader knows what to instantiate fileOut->WriteString( "shadowmodel" ); fileOut->WriteString( token ); } srfTriangles_t* tri = R_AllocStaticTriSurf(); tri->numVerts = src->ParseInt(); tri->numShadowIndexesNoCaps = src->ParseInt(); tri->numShadowIndexesNoFrontCaps = src->ParseInt(); tri->numIndexes = src->ParseInt(); tri->shadowCapPlaneBits = src->ParseInt(); assert( ( tri->numVerts & 1 ) == 0 ); R_AllocStaticTriSurfPreLightShadowVerts( tri, ALIGN( tri->numVerts, 2 ) ); tri->bounds.Clear(); for( int j = 0; j < tri->numVerts; j++ ) { float vec[8]; src->Parse1DMatrix( 3, vec ); tri->preLightShadowVertexes[j].xyzw[0] = vec[0]; tri->preLightShadowVertexes[j].xyzw[1] = vec[1]; tri->preLightShadowVertexes[j].xyzw[2] = vec[2]; tri->preLightShadowVertexes[j].xyzw[3] = 1.0f; // no homogenous value tri->bounds.AddPoint( tri->preLightShadowVertexes[j].xyzw.ToVec3() ); } // clear the last vertex if it wasn't stored if( ( tri->numVerts & 1 ) != 0 ) { tri->preLightShadowVertexes[ALIGN( tri->numVerts, 2 ) - 1].xyzw.Zero(); } // to be consistent set the number of vertices to half the number of shadow vertices tri->numVerts = ALIGN( tri->numVerts, 2 ) / 2; R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for( int j = 0; j < tri->numIndexes; j++ ) { tri->indexes[j] = src->ParseInt(); } // add the completed surface to the model modelSurface_t surf; surf.id = 0; surf.shader = tr.defaultMaterial; surf.geometry = tri; model->AddSurface( surf ); src->ExpectTokenString( "}" ); // NOTE: we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc. if( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) { model->WriteBinaryModel( fileOut, &mapTimeStamp ); } return model; } /* ================ idRenderWorldLocal::SetupAreaRefs ================ */ void idRenderWorldLocal::SetupAreaRefs() { connectedAreaNum = 0; for( int i = 0; i < numPortalAreas; i++ ) { portalAreas[i].areaNum = i; portalAreas[i].lightRefs.areaNext = portalAreas[i].lightRefs.areaPrev = &portalAreas[i].lightRefs; portalAreas[i].entityRefs.areaNext = portalAreas[i].entityRefs.areaPrev = &portalAreas[i].entityRefs; } } /* ================ idRenderWorldLocal::ParseInterAreaPortals ================ */ void idRenderWorldLocal::ParseInterAreaPortals( idLexer* src, idFile* fileOut ) { src->ExpectTokenString( "{" ); numPortalAreas = src->ParseInt(); if( numPortalAreas < 0 ) { src->Error( "R_ParseInterAreaPortals: bad numPortalAreas" ); return; } if( fileOut != NULL ) { // write out the type so the binary reader knows what to instantiate fileOut->WriteString( "interAreaPortals" ); } portalAreas = ( portalArea_t* )R_ClearedStaticAlloc( numPortalAreas * sizeof( portalAreas[0] ) ); areaScreenRect = ( idScreenRect* ) R_ClearedStaticAlloc( numPortalAreas * sizeof( idScreenRect ) ); // set the doubly linked lists SetupAreaRefs(); numInterAreaPortals = src->ParseInt(); if( numInterAreaPortals < 0 ) { src->Error( "R_ParseInterAreaPortals: bad numInterAreaPortals" ); return; } if( fileOut != NULL ) { fileOut->WriteBig( numPortalAreas ); fileOut->WriteBig( numInterAreaPortals ); } doublePortals = ( doublePortal_t* )R_ClearedStaticAlloc( numInterAreaPortals * sizeof( doublePortals [0] ) ); for( int i = 0; i < numInterAreaPortals; i++ ) { int numPoints, a1, a2; idWinding* w; portal_t* p; numPoints = src->ParseInt(); a1 = src->ParseInt(); a2 = src->ParseInt(); if( fileOut != NULL ) { fileOut->WriteBig( numPoints ); fileOut->WriteBig( a1 ); fileOut->WriteBig( a2 ); } w = new( TAG_RENDER_WINDING ) idWinding( numPoints ); w->SetNumPoints( numPoints ); for( int j = 0; j < numPoints; j++ ) { src->Parse1DMatrix( 3, ( *w )[j].ToFloatPtr() ); if( fileOut != NULL ) { fileOut->WriteBig( ( *w )[j].x ); fileOut->WriteBig( ( *w )[j].y ); fileOut->WriteBig( ( *w )[j].z ); } // no texture coordinates ( *w )[j][3] = 0; ( *w )[j][4] = 0; } // add the portal to a1 p = ( portal_t* )R_ClearedStaticAlloc( sizeof( *p ) ); p->intoArea = a2; p->doublePortal = &doublePortals[i]; p->w = w; p->w->GetPlane( p->plane ); p->next = portalAreas[a1].portals; portalAreas[a1].portals = p; doublePortals[i].portals[0] = p; // reverse it for a2 p = ( portal_t* )R_ClearedStaticAlloc( sizeof( *p ) ); p->intoArea = a1; p->doublePortal = &doublePortals[i]; p->w = w->Reverse(); p->w->GetPlane( p->plane ); p->next = portalAreas[a2].portals; portalAreas[a2].portals = p; doublePortals[i].portals[1] = p; } src->ExpectTokenString( "}" ); } /* ================ idRenderWorldLocal::ParseInterAreaPortals ================ */ void idRenderWorldLocal::ReadBinaryAreaPortals( idFile* file ) { file->ReadBig( numPortalAreas ); file->ReadBig( numInterAreaPortals ); portalAreas = ( portalArea_t* )R_ClearedStaticAlloc( numPortalAreas * sizeof( portalAreas[0] ) ); areaScreenRect = ( idScreenRect* ) R_ClearedStaticAlloc( numPortalAreas * sizeof( idScreenRect ) ); // set the doubly linked lists SetupAreaRefs(); doublePortals = ( doublePortal_t* )R_ClearedStaticAlloc( numInterAreaPortals * sizeof( doublePortals [0] ) ); for( int i = 0; i < numInterAreaPortals; i++ ) { int numPoints, a1, a2; idWinding* w; portal_t* p; file->ReadBig( numPoints ); file->ReadBig( a1 ); file->ReadBig( a2 ); w = new( TAG_RENDER_WINDING ) idWinding( numPoints ); w->SetNumPoints( numPoints ); for( int j = 0; j < numPoints; j++ ) { file->ReadBig( ( *w )[ j ][ 0 ] ); file->ReadBig( ( *w )[ j ][ 1 ] ); file->ReadBig( ( *w )[ j ][ 2 ] ); // no texture coordinates ( *w )[ j ][ 3 ] = 0; ( *w )[ j ][ 4 ] = 0; } // add the portal to a1 p = ( portal_t* )R_ClearedStaticAlloc( sizeof( *p ) ); p->intoArea = a2; p->doublePortal = &doublePortals[i]; p->w = w; p->w->GetPlane( p->plane ); p->next = portalAreas[a1].portals; portalAreas[a1].portals = p; doublePortals[i].portals[0] = p; // reverse it for a2 p = ( portal_t* )R_ClearedStaticAlloc( sizeof( *p ) ); p->intoArea = a1; p->doublePortal = &doublePortals[i]; p->w = w->Reverse(); p->w->GetPlane( p->plane ); p->next = portalAreas[a2].portals; portalAreas[a2].portals = p; doublePortals[i].portals[1] = p; } } /* ================ idRenderWorldLocal::ParseNodes ================ */ void idRenderWorldLocal::ParseNodes( idLexer* src, idFile* fileOut ) { src->ExpectTokenString( "{" ); numAreaNodes = src->ParseInt(); if( numAreaNodes < 0 ) { src->Error( "R_ParseNodes: bad numAreaNodes" ); } areaNodes = ( areaNode_t* )R_ClearedStaticAlloc( numAreaNodes * sizeof( areaNodes[0] ) ); if( fileOut != NULL ) { // write out the type so the binary reader knows what to instantiate fileOut->WriteString( "nodes" ); } if( fileOut != NULL ) { fileOut->WriteBig( numAreaNodes ); } for( int i = 0; i < numAreaNodes; i++ ) { areaNode_t* node; node = &areaNodes[i]; src->Parse1DMatrix( 4, node->plane.ToFloatPtr() ); node->children[0] = src->ParseInt(); node->children[1] = src->ParseInt(); if( fileOut != NULL ) { fileOut->WriteBig( node->plane[ 0 ] ); fileOut->WriteBig( node->plane[ 1 ] ); fileOut->WriteBig( node->plane[ 2 ] ); fileOut->WriteBig( node->plane[ 3 ] ); fileOut->WriteBig( node->children[ 0 ] ); fileOut->WriteBig( node->children[ 1 ] ); } } src->ExpectTokenString( "}" ); } /* ================ idRenderWorldLocal::ReadBinaryNodes ================ */ void idRenderWorldLocal::ReadBinaryNodes( idFile* file ) { file->ReadBig( numAreaNodes ); areaNodes = ( areaNode_t* )R_ClearedStaticAlloc( numAreaNodes * sizeof( areaNodes[0] ) ); for( int i = 0; i < numAreaNodes; i++ ) { areaNode_t* node = &areaNodes[ i ]; file->ReadBig( node->plane[ 0 ] ); file->ReadBig( node->plane[ 1 ] ); file->ReadBig( node->plane[ 2 ] ); file->ReadBig( node->plane[ 3 ] ); file->ReadBig( node->children[ 0 ] ); file->ReadBig( node->children[ 1 ] ); } } /* ================ idRenderWorldLocal::CommonChildrenArea_r ================ */ int idRenderWorldLocal::CommonChildrenArea_r( areaNode_t* node ) { int nums[2]; for( int i = 0; i < 2; i++ ) { if( node->children[i] <= 0 ) { nums[i] = -1 - node->children[i]; } else { nums[i] = CommonChildrenArea_r( &areaNodes[ node->children[i] ] ); } } // solid nodes will match any area if( nums[0] == AREANUM_SOLID ) { nums[0] = nums[1]; } if( nums[1] == AREANUM_SOLID ) { nums[1] = nums[0]; } int common; if( nums[0] == nums[1] ) { common = nums[0]; } else { common = CHILDREN_HAVE_MULTIPLE_AREAS; } node->commonChildrenArea = common; return common; } /* ================= idRenderWorldLocal::ClearWorld Sets up for a single area world ================= */ void idRenderWorldLocal::ClearWorld() { numPortalAreas = 1; portalAreas = ( portalArea_t* )R_ClearedStaticAlloc( sizeof( portalAreas[0] ) ); areaScreenRect = ( idScreenRect* ) R_ClearedStaticAlloc( sizeof( idScreenRect ) ); SetupAreaRefs(); // even though we only have a single area, create a node // that has both children pointing at it so we don't need to // areaNodes = ( areaNode_t* )R_ClearedStaticAlloc( sizeof( areaNodes[0] ) ); areaNodes[0].plane[3] = 1; areaNodes[0].children[0] = -1; areaNodes[0].children[1] = -1; } /* ================= idRenderWorldLocal::FreeDefs dump all the interactions ================= */ void idRenderWorldLocal::FreeDefs() { generateAllInteractionsCalled = false; if( interactionTable ) { R_StaticFree( interactionTable ); interactionTable = NULL; } // free all lightDefs for( int i = 0; i < lightDefs.Num(); i++ ) { idRenderLightLocal* light = lightDefs[i]; if( light != NULL && light->world == this ) { FreeLightDef( i ); lightDefs[i] = NULL; } } // free all entityDefs for( int i = 0; i < entityDefs.Num(); i++ ) { idRenderEntityLocal* mod = entityDefs[i]; if( mod != NULL && mod->world == this ) { FreeEntityDef( i ); entityDefs[i] = NULL; } } // Reset decals and overlays for( int i = 0; i < decals.Num(); i++ ) { decals[i].entityHandle = -1; decals[i].lastStartTime = 0; } for( int i = 0; i < overlays.Num(); i++ ) { overlays[i].entityHandle = -1; overlays[i].lastStartTime = 0; } } /* ================= idRenderWorldLocal::InitFromMap A NULL or empty name will make a world without a map model, which is still useful for displaying a bare model ================= */ bool idRenderWorldLocal::InitFromMap( const char* name ) { idLexer* src; idToken token; idRenderModel* lastModel; // if this is an empty world, initialize manually if( !name || !name[0] ) { FreeWorld(); mapName.Clear(); ClearWorld(); return true; } // load it idStrStatic< MAX_OSPATH > filename = name; filename.SetFileExtension( PROC_FILE_EXT ); // check for generated file idStrStatic< MAX_OSPATH > generatedFileName = filename; generatedFileName.Insert( "generated/", 0 ); generatedFileName.SetFileExtension( "bproc" ); // if we are reloading the same map, check the timestamp // and try to skip all the work ID_TIME_T currentTimeStamp = fileSystem->GetTimestamp( filename ); if( name == mapName ) { if( fileSystem->InProductionMode() || ( currentTimeStamp != FILE_NOT_FOUND_TIMESTAMP && currentTimeStamp == mapTimeStamp ) ) { common->Printf( "idRenderWorldLocal::InitFromMap: retaining existing map\n" ); FreeDefs(); TouchWorldModels(); AddWorldModelEntities(); ClearPortalStates(); return true; } common->Printf( "idRenderWorldLocal::InitFromMap: timestamp has changed, reloading.\n" ); } FreeWorld(); // see if we have a generated version of this static const byte BPROC_VERSION = 1; static const unsigned int BPROC_MAGIC = ( 'P' << 24 ) | ( 'R' << 16 ) | ( 'O' << 8 ) | BPROC_VERSION; bool loaded = false; idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) ); if( file != NULL ) { int numEntries = 0; int magic = 0; file->ReadBig( magic ); if( magic == BPROC_MAGIC ) { file->ReadBig( numEntries ); file->ReadString( mapName ); file->ReadBig( mapTimeStamp ); loaded = true; for( int i = 0; i < numEntries; i++ ) { idStrStatic< MAX_OSPATH > type; file->ReadString( type ); type.ToLower(); if( type == "model" ) { idRenderModel* lastModel = ReadBinaryModel( file ); if( lastModel == NULL ) { loaded = false; break; } renderModelManager->AddModel( lastModel ); localModels.Append( lastModel ); } else if( type == "shadowmodel" ) { idRenderModel* lastModel = ReadBinaryModel( file ); if( lastModel == NULL ) { loaded = false; break; } renderModelManager->AddModel( lastModel ); localModels.Append( lastModel ); } else if( type == "interareaportals" ) { ReadBinaryAreaPortals( file ); } else if( type == "nodes" ) { ReadBinaryNodes( file ); } else { idLib::Error( "Binary proc file failed, unexpected type %s\n", type.c_str() ); } } } } if( !loaded ) { src = new( TAG_RENDER ) idLexer( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE ); if( !src->IsLoaded() ) { common->Printf( "idRenderWorldLocal::InitFromMap: %s not found\n", filename.c_str() ); ClearWorld(); return false; } mapName = name; mapTimeStamp = currentTimeStamp; // if we are writing a demo, archive the load command if( common->WriteDemo() ) { WriteLoadMap(); } if( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) { common->Printf( "idRenderWorldLocal::InitFromMap: bad id '%s' instead of '%s'\n", token.c_str(), PROC_FILE_ID ); delete src; return false; } int numEntries = 0; idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) ); if( outputFile != NULL ) { int magic = BPROC_MAGIC; outputFile->WriteBig( magic ); outputFile->WriteBig( numEntries ); outputFile->WriteString( mapName ); outputFile->WriteBig( mapTimeStamp ); } // parse the file while( 1 ) { if( !src->ReadToken( &token ) ) { break; } common->UpdateLevelLoadPacifier(); if( token == "model" ) { lastModel = ParseModel( src, name, currentTimeStamp, outputFile ); // add it to the model manager list renderModelManager->AddModel( lastModel ); // save it in the list to free when clearing this map localModels.Append( lastModel ); numEntries++; continue; } if( token == "shadowModel" ) { lastModel = ParseShadowModel( src, outputFile ); // add it to the model manager list renderModelManager->AddModel( lastModel ); // save it in the list to free when clearing this map localModels.Append( lastModel ); numEntries++; continue; } if( token == "interAreaPortals" ) { ParseInterAreaPortals( src, outputFile ); numEntries++; continue; } if( token == "nodes" ) { ParseNodes( src, outputFile ); numEntries++; continue; } src->Error( "idRenderWorldLocal::InitFromMap: bad token \"%s\"", token.c_str() ); } delete src; if( outputFile != NULL ) { outputFile->Seek( 0, FS_SEEK_SET ); int magic = BPROC_MAGIC; outputFile->WriteBig( magic ); outputFile->WriteBig( numEntries ); } } // if it was a trivial map without any areas, create a single area if( !numPortalAreas ) { ClearWorld(); } // find the points where we can early-our of reference pushing into the BSP tree CommonChildrenArea_r( &areaNodes[0] ); AddWorldModelEntities(); ClearPortalStates(); // done! return true; } /* ===================== idRenderWorldLocal::ClearPortalStates ===================== */ void idRenderWorldLocal::ClearPortalStates() { // all portals start off open for( int i = 0; i < numInterAreaPortals; i++ ) { doublePortals[i].blockingBits = PS_BLOCK_NONE; } // flood fill all area connections for( int i = 0; i < numPortalAreas; i++ ) { for( int j = 0; j < NUM_PORTAL_ATTRIBUTES; j++ ) { connectedAreaNum++; FloodConnectedAreas( &portalAreas[i], j ); } } } /* ===================== idRenderWorldLocal::AddWorldModelEntities ===================== */ void idRenderWorldLocal::AddWorldModelEntities() { // add the world model for each portal area // we can't just call AddEntityDef, because that would place the references // based on the bounding box, rather than explicitly into the correct area for( int i = 0; i < numPortalAreas; i++ ) { common->UpdateLevelLoadPacifier(); idRenderEntityLocal* def = new( TAG_RENDER_ENTITY ) idRenderEntityLocal; // try and reuse a free spot int index = entityDefs.FindNull(); if( index == -1 ) { index = entityDefs.Append( def ); } else { entityDefs[index] = def; } def->index = index; def->world = this; def->parms.hModel = renderModelManager->FindModel( va( "_area%i", i ) ); if( def->parms.hModel->IsDefaultModel() || !def->parms.hModel->IsStaticWorldModel() ) { common->Error( "idRenderWorldLocal::InitFromMap: bad area model lookup" ); } idRenderModel* hModel = def->parms.hModel; for( int j = 0; j < hModel->NumSurfaces(); j++ ) { const modelSurface_t* surf = hModel->Surface( j ); if( surf->shader->GetName() == idStr( "textures/smf/portal_sky" ) ) { def->needsPortalSky = true; } } // the local and global reference bounds are the same for area models def->localReferenceBounds = def->parms.hModel->Bounds(); def->globalReferenceBounds = def->parms.hModel->Bounds(); def->parms.axis[0][0] = 1.0f; def->parms.axis[1][1] = 1.0f; def->parms.axis[2][2] = 1.0f; // in case an explicit shader is used on the world, we don't // want it to have a 0 alpha or color def->parms.shaderParms[0] = 1.0f; def->parms.shaderParms[1] = 1.0f; def->parms.shaderParms[2] = 1.0f; def->parms.shaderParms[3] = 1.0f; R_DeriveEntityData( def ); AddEntityRefToArea( def, &portalAreas[i] ); } } /* ===================== CheckAreaForPortalSky ===================== */ bool idRenderWorldLocal::CheckAreaForPortalSky( int areaNum ) { assert( areaNum >= 0 && areaNum < numPortalAreas ); for( areaReference_t* ref = portalAreas[areaNum].entityRefs.areaNext; ref->entity; ref = ref->areaNext ) { assert( ref->area == &portalAreas[areaNum] ); if( ref->entity && ref->entity->needsPortalSky ) { return true; } } return false; } /* ===================== ResetLocalRenderModels ===================== */ void idRenderWorldLocal::ResetLocalRenderModels() { localModels.Clear(); // Clear out the list when switching between expansion packs, so InitFromMap doesn't try to delete the list whose content has already been deleted by the model manager being re-started }