mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-12-11 21:40:49 +00:00
736ec20d4d
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.
647 lines
16 KiB
C++
647 lines
16 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/Game.h"
|
|
|
|
#include "tools/compilers/dmap/dmap.h"
|
|
|
|
/*
|
|
|
|
After parsing, there will be a list of entities that each has
|
|
a list of primitives.
|
|
|
|
Primitives are either brushes, triangle soups, or model references.
|
|
|
|
Curves are tesselated to triangle soups at load time, but model
|
|
references are
|
|
Brushes will have
|
|
|
|
brushes, each of which has a side definition.
|
|
|
|
*/
|
|
|
|
//
|
|
// private declarations
|
|
//
|
|
|
|
#define MAX_BUILD_SIDES 300
|
|
|
|
static int entityPrimitive; // to track editor brush numbers
|
|
static int c_numMapPatches;
|
|
static int c_areaportals;
|
|
|
|
static uEntity_t *uEntity;
|
|
|
|
// brushes are parsed into a temporary array of sides,
|
|
// which will have duplicates removed before the final brush is allocated
|
|
static uBrush_t *buildBrush;
|
|
|
|
|
|
#define NORMAL_EPSILON 0.00001f
|
|
#define DIST_EPSILON 0.01f
|
|
|
|
|
|
/*
|
|
===========
|
|
FindFloatPlane
|
|
===========
|
|
*/
|
|
int FindFloatPlane( const idPlane &plane, bool *fixedDegeneracies ) {
|
|
idPlane p = plane;
|
|
bool fixed = p.FixDegeneracies( DIST_EPSILON );
|
|
if ( fixed && fixedDegeneracies ) {
|
|
*fixedDegeneracies = true;
|
|
}
|
|
return dmapGlobals.mapPlanes.FindPlane( p, NORMAL_EPSILON, DIST_EPSILON );
|
|
}
|
|
|
|
/*
|
|
===========
|
|
SetBrushContents
|
|
|
|
The contents on all sides of a brush should be the same
|
|
Sets contentsShader, contents, opaque
|
|
===========
|
|
*/
|
|
static void SetBrushContents( uBrush_t *b ) {
|
|
int contents, c2;
|
|
side_t *s;
|
|
int i;
|
|
|
|
s = &b->sides[0];
|
|
contents = s->material->GetContentFlags();
|
|
|
|
b->contentShader = s->material;
|
|
|
|
// a brush is only opaque if all sides are opaque
|
|
b->opaque = true;
|
|
|
|
for ( i=1 ; i<b->numsides ; i++, s++ ) {
|
|
s = &b->sides[i];
|
|
|
|
if ( !s->material ) {
|
|
continue;
|
|
}
|
|
|
|
c2 = s->material->GetContentFlags();
|
|
if (c2 != contents) {
|
|
contents |= c2;
|
|
}
|
|
|
|
if ( s->material->Coverage() != MC_OPAQUE ) {
|
|
b->opaque = false;
|
|
}
|
|
}
|
|
|
|
if ( contents & CONTENTS_AREAPORTAL ) {
|
|
c_areaportals++;
|
|
}
|
|
|
|
b->contents = contents;
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
===============
|
|
FreeBuildBrush
|
|
===============
|
|
*/
|
|
static void FreeBuildBrush( void ) {
|
|
int i;
|
|
|
|
for ( i = 0 ; i < buildBrush->numsides ; i++ ) {
|
|
if ( buildBrush->sides[i].winding ) {
|
|
delete buildBrush->sides[i].winding;
|
|
}
|
|
}
|
|
buildBrush->numsides = 0;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
FinishBrush
|
|
|
|
Produces a final brush based on the buildBrush->sides array
|
|
and links it to the current entity
|
|
===============
|
|
*/
|
|
static uBrush_t *FinishBrush( void ) {
|
|
uBrush_t *b;
|
|
primitive_t *prim;
|
|
|
|
// create windings for sides and bounds for brush
|
|
if ( !CreateBrushWindings( buildBrush ) ) {
|
|
// don't keep this brush
|
|
FreeBuildBrush();
|
|
return NULL;
|
|
}
|
|
|
|
if ( buildBrush->contents & CONTENTS_AREAPORTAL ) {
|
|
if (dmapGlobals.num_entities != 1) {
|
|
common->Printf("Entity %i, Brush %i: areaportals only allowed in world\n"
|
|
, dmapGlobals.num_entities - 1, entityPrimitive);
|
|
FreeBuildBrush();
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// keep it
|
|
b = CopyBrush( buildBrush );
|
|
|
|
FreeBuildBrush();
|
|
|
|
b->entitynum = dmapGlobals.num_entities-1;
|
|
b->brushnum = entityPrimitive;
|
|
|
|
b->original = b;
|
|
|
|
prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) );
|
|
memset( prim, 0, sizeof( *prim ) );
|
|
prim->next = uEntity->primitives;
|
|
uEntity->primitives = prim;
|
|
|
|
prim->brush = b;
|
|
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
================
|
|
AdjustEntityForOrigin
|
|
================
|
|
*/
|
|
#if 0
|
|
static void AdjustEntityForOrigin( uEntity_t *ent ) {
|
|
primitive_t *prim;
|
|
uBrush_t *b;
|
|
int i;
|
|
side_t *s;
|
|
|
|
for ( prim = ent->primitives ; prim ; prim = prim->next ) {
|
|
b = prim->brush;
|
|
if ( !b ) {
|
|
continue;
|
|
}
|
|
for ( i = 0; i < b->numsides; i++ ) {
|
|
idPlane plane;
|
|
|
|
s = &b->sides[i];
|
|
|
|
plane = dmapGlobals.mapPlanes[s->planenum];
|
|
plane[3] += plane.Normal() * ent->origin;
|
|
|
|
s->planenum = FindFloatPlane( plane );
|
|
|
|
s->texVec.v[0][3] += DotProduct( ent->origin, s->texVec.v[0] );
|
|
s->texVec.v[1][3] += DotProduct( ent->origin, s->texVec.v[1] );
|
|
|
|
// remove any integral shift
|
|
s->texVec.v[0][3] -= floor( s->texVec.v[0][3] );
|
|
s->texVec.v[1][3] -= floor( s->texVec.v[1][3] );
|
|
}
|
|
CreateBrushWindings(b);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
=================
|
|
RemoveDuplicateBrushPlanes
|
|
|
|
Returns false if the brush has a mirrored set of planes,
|
|
meaning it encloses no volume.
|
|
Also removes planes without any normal
|
|
=================
|
|
*/
|
|
static bool RemoveDuplicateBrushPlanes( uBrush_t * b ) {
|
|
int i, j, k;
|
|
side_t *sides;
|
|
|
|
sides = b->sides;
|
|
|
|
for ( i = 1 ; i < b->numsides ; i++ ) {
|
|
|
|
// check for a degenerate plane
|
|
if ( sides[i].planenum == -1) {
|
|
common->Printf("Entity %i, Brush %i: degenerate plane\n"
|
|
, b->entitynum, b->brushnum);
|
|
// remove it
|
|
for ( k = i + 1 ; k < b->numsides ; k++ ) {
|
|
sides[k-1] = sides[k];
|
|
}
|
|
b->numsides--;
|
|
i--;
|
|
continue;
|
|
}
|
|
|
|
// check for duplication and mirroring
|
|
for ( j = 0 ; j < i ; j++ ) {
|
|
if ( sides[i].planenum == sides[j].planenum ) {
|
|
common->Printf("Entity %i, Brush %i: duplicate plane\n"
|
|
, b->entitynum, b->brushnum);
|
|
// remove the second duplicate
|
|
for ( k = i + 1 ; k < b->numsides ; k++ ) {
|
|
sides[k-1] = sides[k];
|
|
}
|
|
b->numsides--;
|
|
i--;
|
|
break;
|
|
}
|
|
|
|
if ( sides[i].planenum == (sides[j].planenum ^ 1) ) {
|
|
// mirror plane, brush is invalid
|
|
common->Printf("Entity %i, Brush %i: mirrored plane\n"
|
|
, b->entitynum, b->brushnum);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
ParseBrush
|
|
=================
|
|
*/
|
|
static void ParseBrush( const idMapBrush *mapBrush, int primitiveNum ) {
|
|
uBrush_t *b;
|
|
side_t *s;
|
|
const idMapBrushSide *ms;
|
|
int i;
|
|
bool fixedDegeneracies = false;
|
|
|
|
buildBrush->entitynum = dmapGlobals.num_entities-1;
|
|
buildBrush->brushnum = entityPrimitive;
|
|
buildBrush->numsides = mapBrush->GetNumSides();
|
|
for ( i = 0 ; i < mapBrush->GetNumSides() ; i++ ) {
|
|
s = &buildBrush->sides[i];
|
|
ms = mapBrush->GetSide(i);
|
|
|
|
memset( s, 0, sizeof( *s ) );
|
|
s->planenum = FindFloatPlane( ms->GetPlane(), &fixedDegeneracies );
|
|
s->material = declManager->FindMaterial( ms->GetMaterial() );
|
|
ms->GetTextureVectors( s->texVec.v );
|
|
// remove any integral shift, which will help with grouping
|
|
s->texVec.v[0][3] -= floor( s->texVec.v[0][3] );
|
|
s->texVec.v[1][3] -= floor( s->texVec.v[1][3] );
|
|
}
|
|
|
|
// if there are mirrored planes, the entire brush is invalid
|
|
if ( !RemoveDuplicateBrushPlanes( buildBrush ) ) {
|
|
return;
|
|
}
|
|
|
|
// get the content for the entire brush
|
|
SetBrushContents( buildBrush );
|
|
|
|
b = FinishBrush();
|
|
if ( !b ) {
|
|
return;
|
|
}
|
|
|
|
if ( fixedDegeneracies && dmapGlobals.verboseentities ) {
|
|
common->Warning( "brush %d has degenerate plane equations", primitiveNum );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
ParseSurface
|
|
================
|
|
*/
|
|
static void ParseSurface( const idMapPatch *patch, const idSurface *surface, const idMaterial *material ) {
|
|
int i;
|
|
mapTri_t *tri;
|
|
primitive_t *prim;
|
|
|
|
prim = (primitive_t *)Mem_Alloc( sizeof( *prim ) );
|
|
memset( prim, 0, sizeof( *prim ) );
|
|
prim->next = uEntity->primitives;
|
|
uEntity->primitives = prim;
|
|
|
|
for ( i = 0; i < surface->GetNumIndexes(); i += 3 ) {
|
|
tri = AllocTri();
|
|
tri->v[2] = (*surface)[surface->GetIndexes()[i+0]];
|
|
tri->v[1] = (*surface)[surface->GetIndexes()[i+2]];
|
|
tri->v[0] = (*surface)[surface->GetIndexes()[i+1]];
|
|
tri->material = material;
|
|
tri->next = prim->tris;
|
|
prim->tris = tri;
|
|
}
|
|
|
|
// set merge groups if needed, to prevent multiple sides from being
|
|
// merged into a single surface in the case of gui shaders, mirrors, and autosprites
|
|
if ( material->IsDiscrete() ) {
|
|
for ( tri = prim->tris ; tri ; tri = tri->next ) {
|
|
tri->mergeGroup = (void *)patch;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
ParsePatch
|
|
================
|
|
*/
|
|
static void ParsePatch( const idMapPatch *patch, int primitiveNum ) {
|
|
const idMaterial *mat;
|
|
|
|
if ( dmapGlobals.noCurves ) {
|
|
return;
|
|
}
|
|
|
|
c_numMapPatches++;
|
|
|
|
mat = declManager->FindMaterial( patch->GetMaterial() );
|
|
|
|
idSurface_Patch *cp = new idSurface_Patch( *patch );
|
|
|
|
if ( patch->GetExplicitlySubdivided() ) {
|
|
cp->SubdivideExplicit( patch->GetHorzSubdivisions(), patch->GetVertSubdivisions(), true );
|
|
} else {
|
|
cp->Subdivide( DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_ERROR, DEFAULT_CURVE_MAX_LENGTH, true );
|
|
}
|
|
|
|
ParseSurface( patch, cp, mat );
|
|
|
|
delete cp;
|
|
}
|
|
|
|
/*
|
|
================
|
|
ProcessMapEntity
|
|
================
|
|
*/
|
|
static bool ProcessMapEntity( idMapEntity *mapEnt ) {
|
|
idMapPrimitive *prim;
|
|
|
|
uEntity = &dmapGlobals.uEntities[dmapGlobals.num_entities];
|
|
memset( uEntity, 0, sizeof(*uEntity) );
|
|
uEntity->mapEntity = mapEnt;
|
|
dmapGlobals.num_entities++;
|
|
|
|
for ( entityPrimitive = 0; entityPrimitive < mapEnt->GetNumPrimitives(); entityPrimitive++ ) {
|
|
prim = mapEnt->GetPrimitive(entityPrimitive);
|
|
|
|
if ( prim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
|
|
ParseBrush( static_cast<idMapBrush*>(prim), entityPrimitive );
|
|
}
|
|
else if ( prim->GetType() == idMapPrimitive::TYPE_PATCH ) {
|
|
ParsePatch( static_cast<idMapPatch*>(prim), entityPrimitive );
|
|
}
|
|
}
|
|
|
|
// never put an origin on the world, even if the editor left one there
|
|
if ( dmapGlobals.num_entities != 1 ) {
|
|
uEntity->mapEntity->epairs.GetVector( "origin", "", uEntity->origin );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//===================================================================
|
|
|
|
/*
|
|
==============
|
|
CreateMapLight
|
|
|
|
==============
|
|
*/
|
|
static void CreateMapLight( const idMapEntity *mapEnt ) {
|
|
mapLight_t *light;
|
|
bool dynamic;
|
|
|
|
// designers can add the "noPrelight" flag to signal that
|
|
// the lights will move around, so we don't want
|
|
// to bother chopping up the surfaces under it or creating
|
|
// shadow volumes
|
|
mapEnt->epairs.GetBool( "noPrelight", "0", dynamic );
|
|
if ( dynamic ) {
|
|
return;
|
|
}
|
|
|
|
light = new mapLight_t;
|
|
light->name[0] = '\0';
|
|
light->shadowTris = NULL;
|
|
|
|
// parse parms exactly as the game do
|
|
// use the game's epair parsing code so
|
|
// we can use the same renderLight generation
|
|
gameEdit->ParseSpawnArgsToRenderLight( &mapEnt->epairs, &light->def.parms );
|
|
|
|
R_DeriveLightData( &light->def );
|
|
|
|
// get the name for naming the shadow surfaces
|
|
const char *name;
|
|
|
|
mapEnt->epairs.GetString( "name", "", &name );
|
|
|
|
idStr::Copynz( light->name, name, sizeof( light->name ) );
|
|
if ( !light->name[0] ) {
|
|
common->Error( "Light at (%f,%f,%f) didn't have a name",
|
|
light->def.parms.origin[0], light->def.parms.origin[1], light->def.parms.origin[2] );
|
|
}
|
|
#if 0
|
|
// use the renderer code to get the bounding planes for the light
|
|
// based on all the parameters
|
|
R_RenderLightFrustum( light->parms, light->frustum );
|
|
light->lightShader = light->parms.shader;
|
|
#endif
|
|
|
|
dmapGlobals.mapLights.Append( light );
|
|
|
|
}
|
|
|
|
/*
|
|
==============
|
|
CreateMapLights
|
|
|
|
==============
|
|
*/
|
|
static void CreateMapLights( const idMapFile *dmapFile ) {
|
|
int i;
|
|
const idMapEntity *mapEnt;
|
|
const char *value;
|
|
|
|
for ( i = 0 ; i < dmapFile->GetNumEntities() ; i++ ) {
|
|
mapEnt = dmapFile->GetEntity(i);
|
|
mapEnt->epairs.GetString( "classname", "", &value);
|
|
if ( !idStr::Icmp( value, "light" ) ) {
|
|
CreateMapLight( mapEnt );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
================
|
|
LoadDMapFile
|
|
================
|
|
*/
|
|
bool LoadDMapFile( const char *filename ) {
|
|
primitive_t *prim;
|
|
idBounds mapBounds;
|
|
int brushes, triSurfs;
|
|
int i;
|
|
int size;
|
|
|
|
common->Printf( "--- LoadDMapFile ---\n" );
|
|
common->Printf( "loading %s\n", filename );
|
|
|
|
// load and parse the map file into canonical form
|
|
dmapGlobals.dmapFile = new idMapFile();
|
|
if ( !dmapGlobals.dmapFile->Parse(filename) ) {
|
|
delete dmapGlobals.dmapFile;
|
|
dmapGlobals.dmapFile = NULL;
|
|
common->Warning( "Couldn't load map file: '%s'", filename );
|
|
return false;
|
|
}
|
|
|
|
dmapGlobals.mapPlanes.Clear();
|
|
dmapGlobals.mapPlanes.SetGranularity( 1024 );
|
|
|
|
// process the canonical form into utility form
|
|
dmapGlobals.num_entities = 0;
|
|
c_numMapPatches = 0;
|
|
c_areaportals = 0;
|
|
|
|
size = dmapGlobals.dmapFile->GetNumEntities() * sizeof( dmapGlobals.uEntities[0] );
|
|
dmapGlobals.uEntities = (uEntity_t *)Mem_Alloc( size );
|
|
memset( dmapGlobals.uEntities, 0, size );
|
|
|
|
// allocate a very large temporary brush for building
|
|
// the brushes as they are loaded
|
|
buildBrush = AllocBrush( MAX_BUILD_SIDES );
|
|
|
|
for ( i = 0 ; i < dmapGlobals.dmapFile->GetNumEntities() ; i++ ) {
|
|
ProcessMapEntity( dmapGlobals.dmapFile->GetEntity(i) );
|
|
}
|
|
|
|
CreateMapLights( dmapGlobals.dmapFile );
|
|
|
|
brushes = 0;
|
|
triSurfs = 0;
|
|
|
|
mapBounds.Clear();
|
|
for ( prim = dmapGlobals.uEntities[0].primitives ; prim ; prim = prim->next ) {
|
|
if ( prim->brush ) {
|
|
brushes++;
|
|
mapBounds.AddBounds( prim->brush->bounds );
|
|
} else if ( prim->tris ) {
|
|
triSurfs++;
|
|
}
|
|
}
|
|
|
|
common->Printf( "%5i total world brushes\n", brushes );
|
|
common->Printf( "%5i total world triSurfs\n", triSurfs );
|
|
common->Printf( "%5i patches\n", c_numMapPatches );
|
|
common->Printf( "%5i entities\n", dmapGlobals.num_entities );
|
|
common->Printf( "%5i planes\n", dmapGlobals.mapPlanes.Num() );
|
|
common->Printf( "%5i areaportals\n", c_areaportals );
|
|
common->Printf( "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", mapBounds[0][0], mapBounds[0][1],mapBounds[0][2],
|
|
mapBounds[1][0], mapBounds[1][1], mapBounds[1][2] );
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
================
|
|
FreeOptimizeGroupList
|
|
================
|
|
*/
|
|
void FreeOptimizeGroupList( optimizeGroup_t *groups ) {
|
|
optimizeGroup_t *next;
|
|
|
|
for ( ; groups ; groups = next ) {
|
|
next = groups->nextGroup;
|
|
FreeTriList( groups->triList );
|
|
Mem_Free( groups );
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
FreeDMapFile
|
|
================
|
|
*/
|
|
void FreeDMapFile( void ) {
|
|
int i, j;
|
|
|
|
FreeBrush( buildBrush );
|
|
buildBrush = NULL;
|
|
|
|
// free the entities and brushes
|
|
for ( i = 0 ; i < dmapGlobals.num_entities ; i++ ) {
|
|
uEntity_t *ent;
|
|
primitive_t *prim, *nextPrim;
|
|
|
|
ent = &dmapGlobals.uEntities[i];
|
|
|
|
FreeTree( ent->tree );
|
|
|
|
// free primitives
|
|
for ( prim = ent->primitives ; prim ; prim = nextPrim ) {
|
|
nextPrim = prim->next;
|
|
if ( prim->brush ) {
|
|
FreeBrush( prim->brush );
|
|
}
|
|
if ( prim->tris ) {
|
|
FreeTriList( prim->tris );
|
|
}
|
|
Mem_Free( prim );
|
|
}
|
|
|
|
// free area surfaces
|
|
if ( ent->areas ) {
|
|
for ( j = 0 ; j < ent->numAreas ; j++ ) {
|
|
uArea_t *area;
|
|
|
|
area = &ent->areas[j];
|
|
FreeOptimizeGroupList( area->groups );
|
|
|
|
}
|
|
Mem_Free( ent->areas );
|
|
}
|
|
}
|
|
|
|
Mem_Free( dmapGlobals.uEntities );
|
|
|
|
dmapGlobals.num_entities = 0;
|
|
|
|
// free the map lights
|
|
for ( i = 0; i < dmapGlobals.mapLights.Num(); i++ ) {
|
|
R_FreeLightDefDerivedData( &dmapGlobals.mapLights[i]->def );
|
|
}
|
|
dmapGlobals.mapLights.DeleteContents( true );
|
|
}
|