DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

RenderWorld_load.cpp (27901B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 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.
     23 
     24 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.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 #include "tr_local.h"
     33 
     34 
     35 /*
     36 ================
     37 idRenderWorldLocal::FreeWorld
     38 ================
     39 */
     40 void idRenderWorldLocal::FreeWorld() {
     41 	// this will free all the lightDefs and entityDefs
     42 	FreeDefs();
     43 
     44 	// free all the portals and check light/model references
     45 	for ( int i = 0; i < numPortalAreas; i++ ) {
     46 		portalArea_t	*area;
     47 		portal_t		*portal, *nextPortal;
     48 
     49 		area = &portalAreas[i];
     50 		for ( portal = area->portals; portal; portal = nextPortal ) {
     51 			nextPortal = portal->next;
     52 			delete portal->w;
     53 			R_StaticFree( portal );
     54 		}
     55 
     56 		// there shouldn't be any remaining lightRefs or entityRefs
     57 		if ( area->lightRefs.areaNext != &area->lightRefs ) {
     58 			common->Error( "FreeWorld: unexpected remaining lightRefs" );
     59 		}
     60 		if ( area->entityRefs.areaNext != &area->entityRefs ) {
     61 			common->Error( "FreeWorld: unexpected remaining entityRefs" );
     62 		}
     63 	}
     64 
     65 	if ( portalAreas ) {
     66 		R_StaticFree( portalAreas );
     67 		portalAreas = NULL;
     68 		numPortalAreas = 0;
     69 		R_StaticFree( areaScreenRect );
     70 		areaScreenRect = NULL;
     71 	}
     72 
     73 	if ( doublePortals ) {
     74 		R_StaticFree( doublePortals );
     75 		doublePortals = NULL;
     76 		numInterAreaPortals = 0;
     77 	}
     78 
     79 	if ( areaNodes ) {
     80 		R_StaticFree( areaNodes );
     81 		areaNodes = NULL;
     82 	}
     83 
     84 	// free all the inline idRenderModels 
     85 	for ( int i = 0; i < localModels.Num(); i++ ) {
     86 		renderModelManager->RemoveModel( localModels[i] );
     87 		delete localModels[i];
     88 	}
     89 	localModels.Clear();
     90 
     91 	areaReferenceAllocator.Shutdown();
     92 	interactionAllocator.Shutdown();
     93 
     94 	mapName = "<FREED>";
     95 }
     96 
     97 /*
     98 ================
     99 idRenderWorldLocal::TouchWorldModels
    100 ================
    101 */
    102 void idRenderWorldLocal::TouchWorldModels() {
    103 	for ( int i = 0; i < localModels.Num(); i++ ) {
    104 		renderModelManager->CheckModel( localModels[i]->Name() );
    105 	}
    106 }
    107 
    108 /*
    109 ================
    110 idRenderWorldLocal::ReadBinaryShadowModel
    111 ================
    112 */
    113 idRenderModel *idRenderWorldLocal::ReadBinaryModel( idFile *fileIn ) {
    114 	idStrStatic< MAX_OSPATH > name;
    115 	fileIn->ReadString( name );
    116 	idRenderModel * model = renderModelManager->AllocModel();
    117 	model->InitEmpty( name );
    118 	if ( model->LoadBinaryModel( fileIn, mapTimeStamp ) ) {
    119 		return model;
    120 	}
    121 	return NULL;
    122 }
    123 
    124 extern idCVar r_binaryLoadRenderModels;
    125 
    126 /*
    127 ================
    128 idRenderWorldLocal::ParseModel
    129 ================
    130 */
    131 idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src, const char *mapName, ID_TIME_T mapTimeStamp, idFile *fileOut ) {
    132 	idToken token;
    133 
    134 	src->ExpectTokenString( "{" );
    135 
    136 	// parse the name
    137 	src->ExpectAnyToken( &token );
    138 
    139 	idRenderModel * model = renderModelManager->AllocModel();
    140 	model->InitEmpty( token );
    141 
    142 	if ( fileOut != NULL ) {
    143 		// write out the type so the binary reader knows what to instantiate
    144 		fileOut->WriteString( "shadowmodel" );
    145 		fileOut->WriteString( token );
    146 	}
    147 
    148 	int numSurfaces = src->ParseInt();
    149 	if ( numSurfaces < 0 ) {
    150 		src->Error( "R_ParseModel: bad numSurfaces" );
    151 	}
    152 
    153 	for ( int i = 0; i < numSurfaces; i++ ) {
    154 		src->ExpectTokenString( "{" );
    155 
    156 		src->ExpectAnyToken( &token );
    157 
    158 		modelSurface_t surf;
    159 		surf.shader = declManager->FindMaterial( token );
    160 
    161 		((idMaterial*)surf.shader)->AddReference();
    162 
    163 		srfTriangles_t * tri = R_AllocStaticTriSurf();
    164 		surf.geometry = tri;
    165 
    166 		tri->numVerts = src->ParseInt();
    167 		tri->numIndexes = src->ParseInt();
    168 
    169 		// parse the vertices
    170 		idTempArray<float> verts( tri->numVerts * 8 );
    171 		for ( int j = 0; j < tri->numVerts; j++ ) {
    172 			src->Parse1DMatrix( 8, &verts[j * 8] );
    173 		}
    174 
    175 		// parse the indices
    176 		idTempArray<triIndex_t> indexes( tri->numIndexes );
    177 		for ( int j = 0; j < tri->numIndexes; j++ ) {
    178 			indexes[j] = src->ParseInt();
    179 		}
    180 
    181 #if 1
    182 		// find the island that each vertex belongs to
    183 		idTempArray<int> vertIslands( tri->numVerts );
    184 		idTempArray<bool> trisVisited( tri->numIndexes );
    185 		vertIslands.Zero();
    186 		trisVisited.Zero();
    187 		int numIslands = 0;
    188 		for ( int j = 0; j < tri->numIndexes; j += 3 ) {
    189 			if ( trisVisited[j] ) {
    190 				continue;
    191 			}
    192 
    193 			int islandNum = ++numIslands;
    194 			vertIslands[indexes[j + 0]] = islandNum;
    195 			vertIslands[indexes[j + 1]] = islandNum;
    196 			vertIslands[indexes[j + 2]] = islandNum;
    197 			trisVisited[j] = true;
    198 
    199 			idList<int> queue;
    200 			queue.Append( j );
    201 			for ( int n = 0; n < queue.Num(); n++ ) {
    202 				int t = queue[n];
    203 				for ( int k = 0; k < tri->numIndexes; k += 3 ) {
    204 					if ( trisVisited[k] ) {
    205 						continue;
    206 					}
    207 					bool connected =	indexes[t + 0] == indexes[k + 0] || indexes[t + 0] == indexes[k + 1] || indexes[t + 0] == indexes[k + 2] ||
    208 										indexes[t + 1] == indexes[k + 0] || indexes[t + 1] == indexes[k + 1] || indexes[t + 1] == indexes[k + 2] ||
    209 										indexes[t + 2] == indexes[k + 0] || indexes[t + 2] == indexes[k + 1] || indexes[t + 2] == indexes[k + 2];
    210 					if ( connected ) {
    211 						vertIslands[indexes[k + 0]] = islandNum;
    212 						vertIslands[indexes[k + 1]] = islandNum;
    213 						vertIslands[indexes[k + 2]] = islandNum;
    214 						trisVisited[k] = true;
    215 						queue.Append( k );
    216 					}
    217 				}
    218 			}
    219 		}
    220 
    221 		// center the texture coordinates for each island for maximum 16-bit precision
    222 		for ( int j = 1; j <= numIslands; j++ ) {
    223 			float minS = idMath::INFINITY;
    224 			float minT = idMath::INFINITY;
    225 			float maxS = -idMath::INFINITY;
    226 			float maxT = -idMath::INFINITY;
    227 			for ( int k = 0; k < tri->numVerts; k++ ) {
    228 				if ( vertIslands[k] == j ) {
    229 					minS = Min( minS, verts[k * 8 + 3] );
    230 					maxS = Max( maxS, verts[k * 8 + 3] );
    231 					minT = Min( minT, verts[k * 8 + 4] );
    232 					maxT = Max( maxT, verts[k * 8 + 4] );
    233 				}
    234 			}
    235 			const float averageS = idMath::Ftoi( ( minS + maxS ) * 0.5f );
    236 			const float averageT = idMath::Ftoi( ( minT + maxT ) * 0.5f );
    237 			for ( int k = 0; k < tri->numVerts; k++ ) {
    238 				if ( vertIslands[k] == j ) {
    239 					verts[k * 8 + 3] -= averageS;
    240 					verts[k * 8 + 4] -= averageT;
    241 				}
    242 			}
    243 		}
    244 #endif
    245 
    246 		R_AllocStaticTriSurfVerts( tri, tri->numVerts );
    247 		for ( int j = 0; j < tri->numVerts; j++ ) {
    248 			tri->verts[j].xyz[0] = verts[j * 8 + 0];
    249 			tri->verts[j].xyz[1] = verts[j * 8 + 1];
    250 			tri->verts[j].xyz[2] = verts[j * 8 + 2];
    251 			tri->verts[j].SetTexCoord( verts[j * 8 + 3], verts[j * 8 + 4] );
    252 			tri->verts[j].SetNormal( verts[j * 8 + 5], verts[j * 8 + 6], verts[j * 8 + 7] );
    253 		}
    254 
    255 		R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
    256 		for ( int j = 0; j < tri->numIndexes; j++ ) {
    257 			tri->indexes[j] = indexes[j];
    258 		}
    259 		src->ExpectTokenString( "}" );
    260 
    261 		// add the completed surface to the model
    262 		model->AddSurface( surf );
    263 	}
    264 
    265 	src->ExpectTokenString( "}" );
    266 
    267 	model->FinishSurfaces();
    268 
    269 	if ( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) {
    270 		model->WriteBinaryModel( fileOut, &mapTimeStamp );
    271 	}
    272 
    273 	return model;
    274 }
    275 
    276 /*
    277 ================
    278 idRenderWorldLocal::ReadBinaryShadowModel
    279 ================
    280 */
    281 idRenderModel *idRenderWorldLocal::ReadBinaryShadowModel( idFile *fileIn ) {
    282 	idStrStatic< MAX_OSPATH > name;
    283 	fileIn->ReadString( name );
    284 	idRenderModel * model = renderModelManager->AllocModel();
    285 	model->InitEmpty( name );
    286 	if ( model->LoadBinaryModel( fileIn, mapTimeStamp ) ) {
    287 		return model;
    288 	}
    289 	return NULL;
    290 }
    291 /*
    292 ================
    293 idRenderWorldLocal::ParseShadowModel
    294 ================
    295 */
    296 idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src, idFile *fileOut ) {
    297 	idToken token;
    298 
    299 	src->ExpectTokenString( "{" );
    300 
    301 	// parse the name
    302 	src->ExpectAnyToken( &token );
    303 
    304 	idRenderModel * model = renderModelManager->AllocModel();
    305 	model->InitEmpty( token );
    306 
    307 	if ( fileOut != NULL ) {
    308 		// write out the type so the binary reader knows what to instantiate
    309 		fileOut->WriteString( "shadowmodel" );
    310 		fileOut->WriteString( token );
    311 	}
    312 
    313 	srfTriangles_t * tri = R_AllocStaticTriSurf();
    314 
    315 	tri->numVerts = src->ParseInt();
    316 	tri->numShadowIndexesNoCaps = src->ParseInt();
    317 	tri->numShadowIndexesNoFrontCaps = src->ParseInt();
    318 	tri->numIndexes = src->ParseInt();
    319 	tri->shadowCapPlaneBits = src->ParseInt();
    320 
    321 	assert( ( tri->numVerts & 1 ) == 0 );
    322 
    323 	R_AllocStaticTriSurfPreLightShadowVerts( tri, ALIGN( tri->numVerts, 2 ) );
    324 	tri->bounds.Clear();
    325 	for ( int j = 0; j < tri->numVerts; j++ ) {
    326 		float vec[8];
    327 
    328 		src->Parse1DMatrix( 3, vec );
    329 		tri->preLightShadowVertexes[j].xyzw[0] = vec[0];
    330 		tri->preLightShadowVertexes[j].xyzw[1] = vec[1];
    331 		tri->preLightShadowVertexes[j].xyzw[2] = vec[2];
    332 		tri->preLightShadowVertexes[j].xyzw[3] = 1.0f;		// no homogenous value
    333 
    334 		tri->bounds.AddPoint( tri->preLightShadowVertexes[j].xyzw.ToVec3() );
    335 	}
    336 	// clear the last vertex if it wasn't stored
    337 	if ( ( tri->numVerts & 1 ) != 0 ) {
    338 		tri->preLightShadowVertexes[ALIGN( tri->numVerts, 2 ) - 1].xyzw.Zero();
    339 	}
    340 
    341 	// to be consistent set the number of vertices to half the number of shadow vertices
    342 	tri->numVerts = ALIGN( tri->numVerts, 2 ) / 2;
    343 
    344 	R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
    345 	for ( int j = 0; j < tri->numIndexes; j++ ) {
    346 		tri->indexes[j] = src->ParseInt();
    347 	}
    348 
    349 	// add the completed surface to the model
    350 	modelSurface_t surf;
    351 	surf.id = 0;
    352 	surf.shader = tr.defaultMaterial;
    353 	surf.geometry = tri;
    354 
    355 	model->AddSurface( surf );
    356 
    357 	src->ExpectTokenString( "}" );
    358 
    359 	// NOTE: we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc.
    360 
    361 	if ( fileOut != NULL && model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() ) {
    362 		model->WriteBinaryModel( fileOut, &mapTimeStamp );
    363 	}
    364 
    365 	return model;
    366 }
    367 
    368 /*
    369 ================
    370 idRenderWorldLocal::SetupAreaRefs
    371 ================
    372 */
    373 void idRenderWorldLocal::SetupAreaRefs() {
    374 	connectedAreaNum = 0;
    375 	for ( int i = 0; i < numPortalAreas; i++ ) {
    376 		portalAreas[i].areaNum = i;
    377 		portalAreas[i].lightRefs.areaNext =
    378 		portalAreas[i].lightRefs.areaPrev = &portalAreas[i].lightRefs;
    379 		portalAreas[i].entityRefs.areaNext =
    380 		portalAreas[i].entityRefs.areaPrev = &portalAreas[i].entityRefs;
    381 	}
    382 }
    383 
    384 /*
    385 ================
    386 idRenderWorldLocal::ParseInterAreaPortals
    387 ================
    388 */
    389 void idRenderWorldLocal::ParseInterAreaPortals( idLexer *src, idFile *fileOut ) {
    390 	src->ExpectTokenString( "{" );
    391 
    392 	numPortalAreas = src->ParseInt();
    393 	if ( numPortalAreas < 0 ) {
    394 		src->Error( "R_ParseInterAreaPortals: bad numPortalAreas" );
    395 		return;
    396 	}
    397 
    398 	if ( fileOut != NULL ) {
    399 		// write out the type so the binary reader knows what to instantiate
    400 		fileOut->WriteString( "interAreaPortals" );
    401 	}
    402 
    403 
    404 	portalAreas = (portalArea_t *)R_ClearedStaticAlloc( numPortalAreas * sizeof( portalAreas[0] ) );
    405 	areaScreenRect = (idScreenRect *) R_ClearedStaticAlloc( numPortalAreas * sizeof( idScreenRect ) );
    406 
    407 	// set the doubly linked lists
    408 	SetupAreaRefs();
    409 
    410 	numInterAreaPortals = src->ParseInt();
    411 	if ( numInterAreaPortals < 0 ) {
    412 		src->Error(  "R_ParseInterAreaPortals: bad numInterAreaPortals" );
    413 		return;
    414 	}
    415 
    416 	if ( fileOut != NULL ) {
    417 		fileOut->WriteBig( numPortalAreas );
    418 		fileOut->WriteBig( numInterAreaPortals );
    419 	}
    420 
    421 	doublePortals = (doublePortal_t *)R_ClearedStaticAlloc( numInterAreaPortals * 
    422 		sizeof( doublePortals [0] ) );
    423 
    424 	for ( int i = 0; i < numInterAreaPortals; i++ ) {
    425 		int		numPoints, a1, a2;
    426 		idWinding	*w;
    427 		portal_t	*p;
    428 
    429 		numPoints = src->ParseInt();
    430 		a1 = src->ParseInt();
    431 		a2 = src->ParseInt();
    432 
    433 		if ( fileOut != NULL ) {
    434 			fileOut->WriteBig( numPoints );
    435 			fileOut->WriteBig( a1 );
    436 			fileOut->WriteBig( a2 );
    437 		}
    438 
    439 		w = new (TAG_RENDER_WINDING) idWinding( numPoints );
    440 		w->SetNumPoints( numPoints );
    441 		for ( int j = 0; j < numPoints; j++ ) {
    442 			src->Parse1DMatrix( 3, (*w)[j].ToFloatPtr() );
    443 
    444 			if ( fileOut != NULL ) {
    445 				fileOut->WriteBig( (*w)[j].x );
    446 				fileOut->WriteBig( (*w)[j].y );
    447 				fileOut->WriteBig( (*w)[j].z );
    448 			}
    449 			// no texture coordinates
    450 			(*w)[j][3] = 0;
    451 			(*w)[j][4] = 0;
    452 		}
    453 
    454 		// add the portal to a1
    455 		p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
    456 		p->intoArea = a2;
    457 		p->doublePortal = &doublePortals[i];
    458 		p->w = w;
    459 		p->w->GetPlane( p->plane );
    460 
    461 		p->next = portalAreas[a1].portals;
    462 		portalAreas[a1].portals = p;
    463 
    464 		doublePortals[i].portals[0] = p;
    465 
    466 		// reverse it for a2
    467 		p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
    468 		p->intoArea = a1;
    469 		p->doublePortal = &doublePortals[i];
    470 		p->w = w->Reverse();
    471 		p->w->GetPlane( p->plane );
    472 
    473 		p->next = portalAreas[a2].portals;
    474 		portalAreas[a2].portals = p;
    475 
    476 		doublePortals[i].portals[1] = p;
    477 	}
    478 
    479 	src->ExpectTokenString( "}" );
    480 }
    481 
    482 /*
    483 ================
    484 idRenderWorldLocal::ParseInterAreaPortals
    485 ================
    486 */
    487 void idRenderWorldLocal::ReadBinaryAreaPortals( idFile *file ) {
    488 
    489 	file->ReadBig( numPortalAreas );
    490 	file->ReadBig( numInterAreaPortals );
    491 
    492 	portalAreas = (portalArea_t *)R_ClearedStaticAlloc( numPortalAreas * sizeof( portalAreas[0] ) );
    493 	areaScreenRect = (idScreenRect *) R_ClearedStaticAlloc( numPortalAreas * sizeof( idScreenRect ) );
    494 
    495 	// set the doubly linked lists
    496 	SetupAreaRefs();
    497 
    498 	doublePortals = (doublePortal_t *)R_ClearedStaticAlloc( numInterAreaPortals * sizeof( doublePortals [0] ) );
    499 
    500 	for ( int i = 0; i < numInterAreaPortals; i++ ) {
    501 		int		numPoints, a1, a2;
    502 		idWinding	*w;
    503 		portal_t	*p;
    504 
    505 		file->ReadBig( numPoints );
    506 		file->ReadBig( a1 );
    507 		file->ReadBig( a2 );
    508 		w = new (TAG_RENDER_WINDING) idWinding( numPoints );
    509 		w->SetNumPoints( numPoints );
    510 		for ( int j = 0; j < numPoints; j++ ) {
    511 			file->ReadBig( (*w)[ j ][ 0 ] );
    512 			file->ReadBig( (*w)[ j ][ 1 ] );
    513 			file->ReadBig( (*w)[ j ][ 2 ] );
    514 			// no texture coordinates
    515 			(*w)[ j ][ 3 ] = 0;
    516 			(*w)[ j ][ 4 ] = 0;
    517 		}
    518 
    519 		// add the portal to a1
    520 		p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
    521 		p->intoArea = a2;
    522 		p->doublePortal = &doublePortals[i];
    523 		p->w = w;
    524 		p->w->GetPlane( p->plane );
    525 
    526 		p->next = portalAreas[a1].portals;
    527 		portalAreas[a1].portals = p;
    528 
    529 		doublePortals[i].portals[0] = p;
    530 
    531 		// reverse it for a2
    532 		p = (portal_t *)R_ClearedStaticAlloc( sizeof( *p ) );
    533 		p->intoArea = a1;
    534 		p->doublePortal = &doublePortals[i];
    535 		p->w = w->Reverse();
    536 		p->w->GetPlane( p->plane );
    537 
    538 		p->next = portalAreas[a2].portals;
    539 		portalAreas[a2].portals = p;
    540 
    541 		doublePortals[i].portals[1] = p;
    542 	}
    543 }
    544 
    545 
    546 /*
    547 ================
    548 idRenderWorldLocal::ParseNodes
    549 ================
    550 */
    551 void idRenderWorldLocal::ParseNodes( idLexer *src, idFile *fileOut ) {
    552 	src->ExpectTokenString( "{" );
    553 
    554 	numAreaNodes = src->ParseInt();
    555 	if ( numAreaNodes < 0 ) {
    556 		src->Error( "R_ParseNodes: bad numAreaNodes" );
    557 	}
    558 	areaNodes = (areaNode_t *)R_ClearedStaticAlloc( numAreaNodes * sizeof( areaNodes[0] ) );
    559 
    560 	if ( fileOut != NULL ) {
    561 		// write out the type so the binary reader knows what to instantiate
    562 		fileOut->WriteString( "nodes" );
    563 	}
    564 
    565 	if ( fileOut != NULL ) {
    566 		fileOut->WriteBig( numAreaNodes );
    567 	}
    568 
    569 	for ( int i = 0; i < numAreaNodes; i++ ) {
    570 		areaNode_t	*node;
    571 
    572 		node = &areaNodes[i];
    573 
    574 		src->Parse1DMatrix( 4, node->plane.ToFloatPtr() );
    575 
    576 		node->children[0] = src->ParseInt();
    577 		node->children[1] = src->ParseInt();
    578 
    579 		if ( fileOut != NULL ) {
    580 			fileOut->WriteBig( node->plane[ 0 ] );
    581 			fileOut->WriteBig( node->plane[ 1 ] );
    582 			fileOut->WriteBig( node->plane[ 2 ] );
    583 			fileOut->WriteBig( node->plane[ 3 ] );
    584 			fileOut->WriteBig( node->children[ 0 ] );
    585 			fileOut->WriteBig( node->children[ 1 ] );
    586 		}
    587 
    588 	}
    589 
    590 	src->ExpectTokenString( "}" );
    591 }
    592 
    593 /*
    594 ================
    595 idRenderWorldLocal::ReadBinaryNodes
    596 ================
    597 */
    598 void idRenderWorldLocal::ReadBinaryNodes( idFile * file ) {
    599 	file->ReadBig( numAreaNodes );
    600 	areaNodes = (areaNode_t *)R_ClearedStaticAlloc( numAreaNodes * sizeof( areaNodes[0] ) );
    601 	for ( int i = 0; i < numAreaNodes; i++ ) {
    602 		areaNode_t * node = &areaNodes[ i ];
    603 		file->ReadBig( node->plane[ 0 ] );
    604 		file->ReadBig( node->plane[ 1 ] );
    605 		file->ReadBig( node->plane[ 2 ] );
    606 		file->ReadBig( node->plane[ 3 ] );
    607 		file->ReadBig( node->children[ 0 ] );
    608 		file->ReadBig( node->children[ 1 ] );
    609 	}
    610 }
    611 
    612 /*
    613 ================
    614 idRenderWorldLocal::CommonChildrenArea_r
    615 ================
    616 */
    617 int idRenderWorldLocal::CommonChildrenArea_r( areaNode_t *node ) {
    618 	int	nums[2];
    619 
    620 	for ( int i = 0; i < 2; i++ ) {
    621 		if ( node->children[i] <= 0 ) {
    622 			nums[i] = -1 - node->children[i];
    623 		} else {
    624 			nums[i] = CommonChildrenArea_r( &areaNodes[ node->children[i] ] );
    625 		}
    626 	}
    627 
    628 	// solid nodes will match any area
    629 	if ( nums[0] == AREANUM_SOLID ) {
    630 		nums[0] = nums[1];
    631 	}
    632 	if ( nums[1] == AREANUM_SOLID ) {
    633 		nums[1] = nums[0];
    634 	}
    635 
    636 	int	common;
    637 	if ( nums[0] == nums[1] ) {
    638 		common = nums[0];
    639 	} else {
    640 		common = CHILDREN_HAVE_MULTIPLE_AREAS;
    641 	}
    642 
    643 	node->commonChildrenArea = common;
    644 
    645 	return common;
    646 }
    647 
    648 /*
    649 =================
    650 idRenderWorldLocal::ClearWorld
    651 
    652 Sets up for a single area world
    653 =================
    654 */
    655 void idRenderWorldLocal::ClearWorld() {
    656 	numPortalAreas = 1;
    657 	portalAreas = (portalArea_t *)R_ClearedStaticAlloc( sizeof( portalAreas[0] ) );
    658 	areaScreenRect = (idScreenRect *) R_ClearedStaticAlloc( sizeof( idScreenRect ) );
    659 
    660 	SetupAreaRefs();
    661 
    662 	// even though we only have a single area, create a node
    663 	// that has both children pointing at it so we don't need to
    664 	//
    665 	areaNodes = (areaNode_t *)R_ClearedStaticAlloc( sizeof( areaNodes[0] ) );
    666 	areaNodes[0].plane[3] = 1;
    667 	areaNodes[0].children[0] = -1;
    668 	areaNodes[0].children[1] = -1;
    669 }
    670 
    671 /*
    672 =================
    673 idRenderWorldLocal::FreeDefs
    674 
    675 dump all the interactions
    676 =================
    677 */
    678 void idRenderWorldLocal::FreeDefs() {
    679 	generateAllInteractionsCalled = false;
    680 
    681 	if ( interactionTable ) {
    682 		R_StaticFree( interactionTable );
    683 		interactionTable = NULL;
    684 	}
    685 
    686 	// free all lightDefs
    687 	for ( int i = 0; i < lightDefs.Num(); i++ ) {
    688 		idRenderLightLocal * light = lightDefs[i];
    689 		if ( light != NULL && light->world == this ) {
    690 			FreeLightDef( i );
    691 			lightDefs[i] = NULL;
    692 		}
    693 	}
    694 
    695 	// free all entityDefs
    696 	for ( int i = 0; i < entityDefs.Num(); i++ ) {
    697 		idRenderEntityLocal	* mod = entityDefs[i];
    698 		if ( mod != NULL && mod->world == this ) {
    699 			FreeEntityDef( i );
    700 			entityDefs[i] = NULL;
    701 		}
    702 	}
    703 
    704 	// Reset decals and overlays
    705 	for ( int i = 0; i < decals.Num(); i++ ) {
    706 		decals[i].entityHandle = -1;
    707 		decals[i].lastStartTime = 0;
    708 	}
    709 	for ( int i = 0; i < overlays.Num(); i++ ) {
    710 		overlays[i].entityHandle = -1;
    711 		overlays[i].lastStartTime = 0;
    712 	}
    713 }
    714 
    715 /*
    716 =================
    717 idRenderWorldLocal::InitFromMap
    718 
    719 A NULL or empty name will make a world without a map model, which
    720 is still useful for displaying a bare model
    721 =================
    722 */
    723 bool idRenderWorldLocal::InitFromMap( const char *name ) {
    724 	idLexer *		src;
    725 	idToken			token;
    726 	idRenderModel *	lastModel;
    727 
    728 	// if this is an empty world, initialize manually
    729 	if ( !name || !name[0] ) {
    730 		FreeWorld();
    731 		mapName.Clear();
    732 		ClearWorld();
    733 		return true;
    734 	}
    735 
    736 	// load it
    737 	idStrStatic< MAX_OSPATH > filename = name;
    738 	filename.SetFileExtension( PROC_FILE_EXT );
    739 
    740 	// check for generated file
    741 	idStrStatic< MAX_OSPATH > generatedFileName = filename;
    742 	generatedFileName.Insert( "generated/", 0 );
    743 	generatedFileName.SetFileExtension( "bproc" );
    744 
    745 	// if we are reloading the same map, check the timestamp
    746 	// and try to skip all the work
    747 	ID_TIME_T currentTimeStamp = fileSystem->GetTimestamp( filename );
    748 
    749 	if ( name == mapName ) {
    750 		if ( fileSystem->InProductionMode() || ( currentTimeStamp != FILE_NOT_FOUND_TIMESTAMP && currentTimeStamp == mapTimeStamp ) ) {
    751 			common->Printf( "idRenderWorldLocal::InitFromMap: retaining existing map\n" );
    752 			FreeDefs();
    753 			TouchWorldModels();
    754 			AddWorldModelEntities();
    755 			ClearPortalStates();
    756 			return true;
    757 		}
    758 		common->Printf( "idRenderWorldLocal::InitFromMap: timestamp has changed, reloading.\n" );
    759 	}
    760 
    761 	FreeWorld();
    762 
    763 	// see if we have a generated version of this 
    764 	static const byte BPROC_VERSION = 1;
    765 	static const unsigned int BPROC_MAGIC = ( 'P' << 24 ) | ( 'R' << 16 ) | ( 'O' << 8 ) | BPROC_VERSION;
    766 	bool loaded = false;
    767 	idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
    768 	if ( file != NULL ) {
    769 		int numEntries = 0;
    770 		int magic = 0;
    771 		file->ReadBig( magic );
    772 		if ( magic == BPROC_MAGIC ) {
    773 			file->ReadBig( numEntries );
    774 			file->ReadString( mapName );
    775 			file->ReadBig( mapTimeStamp );
    776 			loaded = true;
    777 			for ( int i = 0; i < numEntries; i++ ) {
    778 				idStrStatic< MAX_OSPATH > type;
    779 				file->ReadString( type );
    780 				type.ToLower();
    781 				if ( type == "model" ) {
    782 					idRenderModel * lastModel = ReadBinaryModel( file );
    783 					if ( lastModel == NULL ) {
    784 						loaded = false;
    785 						break;
    786 					}
    787 					renderModelManager->AddModel( lastModel );
    788 					localModels.Append( lastModel );
    789 				} else if ( type == "shadowmodel" ) {
    790 					idRenderModel * lastModel = ReadBinaryModel( file );
    791 					if ( lastModel == NULL ) {
    792 						loaded = false;
    793 						break;
    794 					}
    795 					renderModelManager->AddModel( lastModel );
    796 					localModels.Append( lastModel );
    797 				} else if ( type == "interareaportals" ) {
    798 					ReadBinaryAreaPortals( file );
    799 				} else if ( type == "nodes" ) {
    800 					ReadBinaryNodes( file );
    801 				} else {
    802 					idLib::Error( "Binary proc file failed, unexpected type %s\n", type.c_str() );
    803 				}
    804 			}
    805 		}
    806 	}
    807 
    808 	if ( !loaded ) {
    809 
    810 		src = new (TAG_RENDER) idLexer( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
    811 		if ( !src->IsLoaded() ) {
    812 			common->Printf( "idRenderWorldLocal::InitFromMap: %s not found\n", filename.c_str() );
    813 			ClearWorld();
    814 			return false;
    815 		}
    816 
    817 
    818 		mapName = name;
    819 		mapTimeStamp = currentTimeStamp;
    820 
    821 		// if we are writing a demo, archive the load command
    822 		if ( common->WriteDemo() ) {
    823 			WriteLoadMap();
    824 		}
    825 
    826 		if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) {
    827 			common->Printf( "idRenderWorldLocal::InitFromMap: bad id '%s' instead of '%s'\n", token.c_str(), PROC_FILE_ID );
    828 			delete src;
    829 			return false;
    830 		}
    831 			
    832 		int numEntries = 0;
    833 		idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
    834 		if ( outputFile != NULL ) {
    835 			int magic = BPROC_MAGIC;
    836 			outputFile->WriteBig( magic );
    837 			outputFile->WriteBig( numEntries );
    838 			outputFile->WriteString( mapName );
    839 			outputFile->WriteBig( mapTimeStamp );
    840 		}
    841 
    842 		// parse the file
    843 		while ( 1 ) {
    844 			if ( !src->ReadToken( &token ) ) {
    845 				break;
    846 			}
    847 
    848 			common->UpdateLevelLoadPacifier();
    849 
    850 
    851 			if ( token == "model" ) {
    852 				lastModel = ParseModel( src, name, currentTimeStamp, outputFile );
    853 
    854 				// add it to the model manager list
    855 				renderModelManager->AddModel( lastModel );
    856 
    857 				// save it in the list to free when clearing this map
    858 				localModels.Append( lastModel );
    859 
    860 				numEntries++;
    861 
    862 				continue;
    863 			}
    864 
    865 			if ( token == "shadowModel" ) {
    866 				lastModel = ParseShadowModel( src, outputFile );
    867 
    868 				// add it to the model manager list
    869 				renderModelManager->AddModel( lastModel );
    870 
    871 				// save it in the list to free when clearing this map
    872 				localModels.Append( lastModel );
    873 
    874 				numEntries++;
    875 				continue;
    876 			}
    877 
    878 			if ( token == "interAreaPortals" ) {
    879 				ParseInterAreaPortals( src, outputFile );
    880 
    881 				numEntries++;
    882 				continue;
    883 			}
    884 
    885 			if ( token == "nodes" ) {
    886 				ParseNodes( src, outputFile );
    887 
    888 				numEntries++;
    889 				continue;
    890 			}
    891 
    892 			src->Error( "idRenderWorldLocal::InitFromMap: bad token \"%s\"", token.c_str() );
    893 		}
    894 
    895 		delete src;
    896 
    897 		if ( outputFile != NULL ) {
    898 			outputFile->Seek( 0, FS_SEEK_SET );
    899 			int magic = BPROC_MAGIC;
    900 			outputFile->WriteBig( magic );
    901 			outputFile->WriteBig( numEntries );
    902 		}
    903 
    904 	}
    905 
    906 
    907 
    908 	// if it was a trivial map without any areas, create a single area
    909 	if ( !numPortalAreas ) {
    910 		ClearWorld();
    911 	}
    912 
    913 	// find the points where we can early-our of reference pushing into the BSP tree
    914 	CommonChildrenArea_r( &areaNodes[0] );
    915 
    916 	AddWorldModelEntities();
    917 	ClearPortalStates();
    918 
    919 	// done!
    920 	return true;
    921 }
    922 
    923 /*
    924 =====================
    925 idRenderWorldLocal::ClearPortalStates
    926 =====================
    927 */
    928 void idRenderWorldLocal::ClearPortalStates() {
    929 	// all portals start off open
    930 	for ( int i = 0; i < numInterAreaPortals; i++ ) {
    931 		doublePortals[i].blockingBits = PS_BLOCK_NONE;
    932 	}
    933 
    934 	// flood fill all area connections
    935 	for ( int i = 0; i < numPortalAreas; i++ ) {
    936 		for ( int j = 0; j < NUM_PORTAL_ATTRIBUTES; j++ ) {
    937 			connectedAreaNum++;
    938 			FloodConnectedAreas( &portalAreas[i], j );
    939 		}
    940 	}
    941 }
    942 
    943 /*
    944 =====================
    945 idRenderWorldLocal::AddWorldModelEntities
    946 =====================
    947 */
    948 void idRenderWorldLocal::AddWorldModelEntities() {
    949 	// add the world model for each portal area
    950 	// we can't just call AddEntityDef, because that would place the references
    951 	// based on the bounding box, rather than explicitly into the correct area
    952 	for ( int i = 0; i < numPortalAreas; i++ ) {
    953 		common->UpdateLevelLoadPacifier();
    954 
    955 
    956 		idRenderEntityLocal	* def = new (TAG_RENDER_ENTITY) idRenderEntityLocal;
    957 
    958 		// try and reuse a free spot
    959 		int index = entityDefs.FindNull();
    960 		if ( index == -1 ) {
    961 			index = entityDefs.Append(def);
    962 		} else {
    963 			entityDefs[index] = def;
    964 		}
    965 
    966 		def->index = index;
    967 		def->world = this;
    968 
    969 		def->parms.hModel = renderModelManager->FindModel( va("_area%i", i ) );
    970 		if ( def->parms.hModel->IsDefaultModel() || !def->parms.hModel->IsStaticWorldModel() ) {
    971 			common->Error( "idRenderWorldLocal::InitFromMap: bad area model lookup" );
    972 		}
    973 
    974 		idRenderModel *hModel = def->parms.hModel;
    975 
    976 		for ( int j = 0; j < hModel->NumSurfaces(); j++ ) {
    977 			const modelSurface_t *surf = hModel->Surface( j );
    978 
    979 			if ( surf->shader->GetName() == idStr( "textures/smf/portal_sky" ) ) {
    980 				def->needsPortalSky = true;
    981 			}
    982 		}
    983 
    984 		// the local and global reference bounds are the same for area models
    985 		def->localReferenceBounds = def->parms.hModel->Bounds();
    986 		def->globalReferenceBounds = def->parms.hModel->Bounds();
    987 
    988 		def->parms.axis[0][0] = 1.0f;
    989 		def->parms.axis[1][1] = 1.0f;
    990 		def->parms.axis[2][2] = 1.0f;
    991 
    992 		// in case an explicit shader is used on the world, we don't
    993 		// want it to have a 0 alpha or color
    994 		def->parms.shaderParms[0] = 1.0f;
    995 		def->parms.shaderParms[1] = 1.0f;
    996 		def->parms.shaderParms[2] = 1.0f;
    997 		def->parms.shaderParms[3] = 1.0f;
    998 
    999 		R_DeriveEntityData( def );
   1000 
   1001 		AddEntityRefToArea( def, &portalAreas[i] );
   1002 	}
   1003 }
   1004 
   1005 /*
   1006 =====================
   1007 CheckAreaForPortalSky
   1008 =====================
   1009 */
   1010 bool idRenderWorldLocal::CheckAreaForPortalSky( int areaNum ) {
   1011 	assert( areaNum >= 0 && areaNum < numPortalAreas );
   1012 
   1013 	for ( areaReference_t * ref = portalAreas[areaNum].entityRefs.areaNext; ref->entity; ref = ref->areaNext ) {
   1014 		assert( ref->area == &portalAreas[areaNum] );
   1015 
   1016 		if ( ref->entity && ref->entity->needsPortalSky ) {
   1017 			return true;
   1018 		}
   1019 	}
   1020 
   1021 	return false;
   1022 }
   1023 
   1024 /*
   1025 =====================
   1026 ResetLocalRenderModels
   1027 =====================
   1028 */
   1029 void idRenderWorldLocal::ResetLocalRenderModels() {
   1030 	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
   1031 }