dhewm3/neo/tools/compilers/dmap/map.cpp

648 lines
16 KiB
C++
Raw Normal View History

2011-11-22 21:28:15 +00:00
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
2011-11-22 21:28:15 +00:00
2011-12-06 16:14:59 +00:00
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
2011-11-22 21:28:15 +00:00
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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "dmap.h"
/*
After parsing, there will be a list of entities that each has
a list of primitives.
2011-11-22 21:28:15 +00:00
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
2011-11-22 21:28:15 +00:00
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
2011-11-22 21:28:15 +00:00
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;
2011-11-22 21:28:15 +00:00
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
2011-11-22 21:28:15 +00:00
/*
=================
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 ) {
2011-11-22 21:28:15 +00:00
primitive_t *prim;
idBounds mapBounds;
int brushes, triSurfs;
int i;
int size;
common->Printf( "--- LoadDMapFile ---\n" );
common->Printf( "loading %s\n", filename );
2011-11-22 21:28:15 +00:00
// 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 );
}