dhewm3/neo/renderer/RenderWorld_load.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

701 lines
17 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 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 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.
===========================================================================
*/
#include "sys/platform.h"
#include "framework/Session.h"
#include "renderer/ModelManager.h"
#include "renderer/RenderWorld_local.h"
#include "renderer/tr_local.h"
/*
================
idRenderWorldLocal::FreeWorld
================
*/
void idRenderWorldLocal::FreeWorld() {
int i;
// this will free all the lightDefs and entityDefs
FreeDefs();
// free all the portals and check light/model references
for ( 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 ( i = 0 ; i < localModels.Num() ; i++ ) {
renderModelManager->RemoveModel( localModels[i] );
delete localModels[i];
}
localModels.Clear();
areaReferenceAllocator.Shutdown();
interactionAllocator.Shutdown();
areaNumRefAllocator.Shutdown();
mapName = "<FREED>";
}
/*
================
idRenderWorldLocal::TouchWorldModels
================
*/
void idRenderWorldLocal::TouchWorldModels( void ) {
int i;
for ( i = 0 ; i < localModels.Num() ; i++ ) {
renderModelManager->CheckModel( localModels[i]->Name() );
}
}
/*
================
idRenderWorldLocal::ParseModel
================
*/
idRenderModel *idRenderWorldLocal::ParseModel( idLexer *src ) {
idRenderModel *model;
idToken token;
int i, j;
srfTriangles_t *tri;
modelSurface_t surf;
src->ExpectTokenString( "{" );
// parse the name
src->ExpectAnyToken( &token );
model = renderModelManager->AllocModel();
model->InitEmpty( token );
int numSurfaces = src->ParseInt();
if ( numSurfaces < 0 ) {
src->Error( "R_ParseModel: bad numSurfaces" );
}
for ( i = 0 ; i < numSurfaces ; i++ ) {
src->ExpectTokenString( "{" );
src->ExpectAnyToken( &token );
surf.shader = declManager->FindMaterial( token );
((idMaterial*)surf.shader)->AddReference();
tri = R_AllocStaticTriSurf();
surf.geometry = tri;
tri->numVerts = src->ParseInt();
tri->numIndexes = src->ParseInt();
R_AllocStaticTriSurfVerts( tri, tri->numVerts );
for ( j = 0 ; j < tri->numVerts ; j++ ) {
float vec[8];
src->Parse1DMatrix( 8, vec );
tri->verts[j].xyz[0] = vec[0];
tri->verts[j].xyz[1] = vec[1];
tri->verts[j].xyz[2] = vec[2];
tri->verts[j].st[0] = vec[3];
tri->verts[j].st[1] = vec[4];
tri->verts[j].normal[0] = vec[5];
tri->verts[j].normal[1] = vec[6];
tri->verts[j].normal[2] = vec[7];
}
R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
tri->indexes[j] = src->ParseInt();
}
src->ExpectTokenString( "}" );
// add the completed surface to the model
model->AddSurface( surf );
}
src->ExpectTokenString( "}" );
model->FinishSurfaces();
return model;
}
/*
================
idRenderWorldLocal::ParseShadowModel
================
*/
idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) {
idRenderModel *model;
idToken token;
int j;
srfTriangles_t *tri;
modelSurface_t surf;
src->ExpectTokenString( "{" );
// parse the name
src->ExpectAnyToken( &token );
model = renderModelManager->AllocModel();
model->InitEmpty( token );
surf.shader = tr.defaultMaterial;
tri = R_AllocStaticTriSurf();
surf.geometry = tri;
tri->numVerts = src->ParseInt();
tri->numShadowIndexesNoCaps = src->ParseInt();
tri->numShadowIndexesNoFrontCaps = src->ParseInt();
tri->numIndexes = src->ParseInt();
tri->shadowCapPlaneBits = src->ParseInt();
R_AllocStaticTriSurfShadowVerts( tri, tri->numVerts );
tri->bounds.Clear();
for ( j = 0 ; j < tri->numVerts ; j++ ) {
float vec[8];
src->Parse1DMatrix( 3, vec );
tri->shadowVertexes[j].xyz[0] = vec[0];
tri->shadowVertexes[j].xyz[1] = vec[1];
tri->shadowVertexes[j].xyz[2] = vec[2];
tri->shadowVertexes[j].xyz[3] = 1; // no homogenous value
tri->bounds.AddPoint( tri->shadowVertexes[j].xyz.ToVec3() );
}
R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
tri->indexes[j] = src->ParseInt();
}
// add the completed surface to the model
model->AddSurface( surf );
src->ExpectTokenString( "}" );
// we do NOT do a model->FinishSurfaceces, because we don't need sil edges, planes, tangents, etc.
// model->FinishSurfaces();
return model;
}
/*
================
idRenderWorldLocal::SetupAreaRefs
================
*/
void idRenderWorldLocal::SetupAreaRefs() {
int i;
connectedAreaNum = 0;
for ( 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 ) {
int i, j;
src->ExpectTokenString( "{" );
numPortalAreas = src->ParseInt();
if ( numPortalAreas < 0 ) {
src->Error( "R_ParseInterAreaPortals: bad numPortalAreas" );
return;
}
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;
}
doublePortals = (doublePortal_t *)R_ClearedStaticAlloc( numInterAreaPortals *
sizeof( doublePortals [0] ) );
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
int numPoints, a1, a2;
idWinding *w;
portal_t *p;
numPoints = src->ParseInt();
a1 = src->ParseInt();
a2 = src->ParseInt();
w = new idWinding( numPoints );
w->SetNumPoints( numPoints );
for ( j = 0 ; j < numPoints ; j++ ) {
src->Parse1DMatrix( 3, (*w)[j].ToFloatPtr() );
// 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::ParseNodes
================
*/
void idRenderWorldLocal::ParseNodes( idLexer *src ) {
int i;
src->ExpectTokenString( "{" );
numAreaNodes = src->ParseInt();
if ( numAreaNodes < 0 ) {
src->Error( "R_ParseNodes: bad numAreaNodes" );
}
areaNodes = (areaNode_t *)R_ClearedStaticAlloc( numAreaNodes * sizeof( areaNodes[0] ) );
for ( 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();
}
src->ExpectTokenString( "}" );
}
/*
================
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() {
int i;
generateAllInteractionsCalled = false;
if ( interactionTable ) {
R_StaticFree( interactionTable );
interactionTable = NULL;
}
// free all lightDefs
for ( i = 0 ; i < lightDefs.Num() ; i++ ) {
idRenderLightLocal *light;
light = lightDefs[i];
if ( light && light->world == this ) {
FreeLightDef( i );
lightDefs[i] = NULL;
}
}
// free all entityDefs
for ( i = 0 ; i < entityDefs.Num() ; i++ ) {
idRenderEntityLocal *mod;
mod = entityDefs[i];
if ( mod && mod->world == this ) {
FreeEntityDef( i );
entityDefs[i] = NULL;
}
}
}
/*
=================
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;
idStr filename;
idRenderModel * lastModel;
// if this is an empty world, initialize manually
if ( !name || !name[0] ) {
FreeWorld();
mapName.Clear();
ClearWorld();
return true;
}
// load it
filename = name;
filename.SetFileExtension( PROC_FILE_EXT );
// if we are reloading the same map, check the timestamp
// and try to skip all the work
ID_TIME_T currentTimeStamp;
fileSystem->ReadFile( filename, NULL, &currentTimeStamp );
if ( name == mapName ) {
if ( 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();
src = new 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 ( session->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;
}
// parse the file
while ( 1 ) {
if ( !src->ReadToken( &token ) ) {
break;
}
if ( token == "model" ) {
lastModel = ParseModel( src );
// add it to the model manager list
renderModelManager->AddModel( lastModel );
// save it in the list to free when clearing this map
localModels.Append( lastModel );
continue;
}
if ( token == "shadowModel" ) {
lastModel = ParseShadowModel( src );
// add it to the model manager list
renderModelManager->AddModel( lastModel );
// save it in the list to free when clearing this map
localModels.Append( lastModel );
continue;
}
if ( token == "interAreaPortals" ) {
ParseInterAreaPortals( src );
continue;
}
if ( token == "nodes" ) {
ParseNodes( src );
continue;
}
src->Error( "idRenderWorldLocal::InitFromMap: bad token \"%s\"", token.c_str() );
}
delete src;
// 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() {
int i, j;
// all portals start off open
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
doublePortals[i].blockingBits = PS_BLOCK_NONE;
}
// flood fill all area connections
for ( i = 0 ; i < numPortalAreas ; i++ ) {
for ( j = 0 ; j < NUM_PORTAL_ATTRIBUTES ; j++ ) {
connectedAreaNum++;
FloodConnectedAreas( &portalAreas[i], j );
}
}
}
/*
=====================
idRenderWorldLocal::AddWorldModelEntities
=====================
*/
void idRenderWorldLocal::AddWorldModelEntities() {
int i;
// 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 ( i = 0 ; i < numPortalAreas ; i++ ) {
idRenderEntityLocal *def;
int index;
def = new idRenderEntityLocal;
// try and reuse a free spot
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;
}
}
def->referenceBounds = def->parms.hModel->Bounds();
def->parms.axis[0][0] = 1;
def->parms.axis[1][1] = 1;
def->parms.axis[2][2] = 1;
R_AxisToModelMatrix( def->parms.axis, def->parms.origin, def->modelMatrix );
// 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] =
def->parms.shaderParms[1] =
def->parms.shaderParms[2] =
def->parms.shaderParms[3] = 1;
AddEntityRefToArea( def, &portalAreas[i] );
}
}
/*
=====================
CheckAreaForPortalSky
=====================
*/
bool idRenderWorldLocal::CheckAreaForPortalSky( int areaNum ) {
areaReference_t *ref;
assert( areaNum >= 0 && areaNum < numPortalAreas );
for ( 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;
}