quadrilateralcowboy/tools/compilers/dmap/map.cpp
2020-06-12 14:06:25 -07:00

648 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 "../../../idlib/precompiled.h"
#pragma hdrstop
#include "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;
bool mixed;
s = &b->sides[0];
contents = s->material->GetContentFlags();
b->contentShader = s->material;
mixed = false;
// 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) {
mixed = true;
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
================
*/
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);
}
}
/*
=================
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 );
}