hello world

This commit is contained in:
Timothee 'TTimo' Besset
2011-11-22 15:28:15 -06:00
commit fb1609f554
2155 changed files with 1017022 additions and 0 deletions

View File

@@ -0,0 +1,404 @@
/*
===========================================================================
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"
dmapGlobals_t dmapGlobals;
/*
============
ProcessModel
============
*/
bool ProcessModel( uEntity_t *e, bool floodFill ) {
bspface_t *faces;
// build a bsp tree using all of the sides
// of all of the structural brushes
faces = MakeStructuralBspFaceList ( e->primitives );
e->tree = FaceBSP( faces );
// create portals at every leaf intersection
// to allow flood filling
MakeTreePortals( e->tree );
// classify the leafs as opaque or areaportal
FilterBrushesIntoTree( e );
// see if the bsp is completely enclosed
if ( floodFill && !dmapGlobals.noFlood ) {
if ( FloodEntities( e->tree ) ) {
// set the outside leafs to opaque
FillOutside( e );
} else {
common->Printf ( "**********************\n" );
common->Warning( "******* leaked *******" );
common->Printf ( "**********************\n" );
LeakFile( e->tree );
// bail out here. If someone really wants to
// process a map that leaks, they should use
// -noFlood
return false;
}
}
// get minimum convex hulls for each visible side
// this must be done before creating area portals,
// because the visible hull is used as the portal
ClipSidesByTree( e );
// determine areas before clipping tris into the
// tree, so tris will never cross area boundaries
FloodAreas( e );
// we now have a BSP tree with solid and non-solid leafs marked with areas
// all primitives will now be clipped into this, throwing away
// fragments in the solid areas
PutPrimitivesInAreas( e );
// now build shadow volumes for the lights and split
// the optimize lists by the light beam trees
// so there won't be unneeded overdraw in the static
// case
Prelight( e );
// optimizing is a superset of fixing tjunctions
if ( !dmapGlobals.noOptimize ) {
OptimizeEntity( e );
} else if ( !dmapGlobals.noTJunc ) {
FixEntityTjunctions( e );
}
// now fix t junctions across areas
FixGlobalTjunctions( e );
return true;
}
/*
============
ProcessModels
============
*/
bool ProcessModels( void ) {
bool oldVerbose;
uEntity_t *entity;
oldVerbose = dmapGlobals.verbose;
for ( dmapGlobals.entityNum = 0 ; dmapGlobals.entityNum < dmapGlobals.num_entities ; dmapGlobals.entityNum++ ) {
entity = &dmapGlobals.uEntities[dmapGlobals.entityNum];
if ( !entity->primitives ) {
continue;
}
common->Printf( "############### entity %i ###############\n", dmapGlobals.entityNum );
// if we leaked, stop without any more processing
if ( !ProcessModel( entity, (bool)(dmapGlobals.entityNum == 0 ) ) ) {
return false;
}
// we usually don't want to see output for submodels unless
// something strange is going on
if ( !dmapGlobals.verboseentities ) {
dmapGlobals.verbose = false;
}
}
dmapGlobals.verbose = oldVerbose;
return true;
}
/*
============
DmapHelp
============
*/
void DmapHelp( void ) {
common->Printf(
"Usage: dmap [options] mapfile\n"
"Options:\n"
"noCurves = don't process curves\n"
"noCM = don't create collision map\n"
"noAAS = don't create AAS files\n"
);
}
/*
============
ResetDmapGlobals
============
*/
void ResetDmapGlobals( void ) {
dmapGlobals.mapFileBase[0] = '\0';
dmapGlobals.dmapFile = NULL;
dmapGlobals.mapPlanes.Clear();
dmapGlobals.num_entities = 0;
dmapGlobals.uEntities = NULL;
dmapGlobals.entityNum = 0;
dmapGlobals.mapLights.Clear();
dmapGlobals.verbose = false;
dmapGlobals.glview = false;
dmapGlobals.noOptimize = false;
dmapGlobals.verboseentities = false;
dmapGlobals.noCurves = false;
dmapGlobals.fullCarve = false;
dmapGlobals.noModelBrushes = false;
dmapGlobals.noTJunc = false;
dmapGlobals.nomerge = false;
dmapGlobals.noFlood = false;
dmapGlobals.noClipSides = false;
dmapGlobals.noLightCarve = false;
dmapGlobals.noShadow = false;
dmapGlobals.shadowOptLevel = SO_NONE;
dmapGlobals.drawBounds.Clear();
dmapGlobals.drawflag = false;
dmapGlobals.totalShadowTriangles = 0;
dmapGlobals.totalShadowVerts = 0;
}
/*
============
Dmap
============
*/
void Dmap( const idCmdArgs &args ) {
int i;
int start, end;
char path[1024];
idStr passedName;
bool leaked = false;
bool noCM = false;
bool noAAS = false;
ResetDmapGlobals();
if ( args.Argc() < 2 ) {
DmapHelp();
return;
}
common->Printf("---- dmap ----\n");
dmapGlobals.fullCarve = true;
dmapGlobals.shadowOptLevel = SO_MERGE_SURFACES; // create shadows by merging all surfaces, but no super optimization
// dmapGlobals.shadowOptLevel = SO_CLIP_OCCLUDERS; // remove occluders that are completely covered
// dmapGlobals.shadowOptLevel = SO_SIL_OPTIMIZE;
// dmapGlobals.shadowOptLevel = SO_CULL_OCCLUDED;
dmapGlobals.noLightCarve = true;
for ( i = 1 ; i < args.Argc() ; i++ ) {
const char *s;
s = args.Argv(i);
if ( s[0] == '-' ) {
s++;
if ( s[0] == '\0' ) {
continue;
}
}
if ( !idStr::Icmp( s,"glview" ) ) {
dmapGlobals.glview = true;
} else if ( !idStr::Icmp( s, "v" ) ) {
common->Printf( "verbose = true\n" );
dmapGlobals.verbose = true;
} else if ( !idStr::Icmp( s, "draw" ) ) {
common->Printf( "drawflag = true\n" );
dmapGlobals.drawflag = true;
} else if ( !idStr::Icmp( s, "noFlood" ) ) {
common->Printf( "noFlood = true\n" );
dmapGlobals.noFlood = true;
} else if ( !idStr::Icmp( s, "noLightCarve" ) ) {
common->Printf( "noLightCarve = true\n" );
dmapGlobals.noLightCarve = true;
} else if ( !idStr::Icmp( s, "lightCarve" ) ) {
common->Printf( "noLightCarve = false\n" );
dmapGlobals.noLightCarve = false;
} else if ( !idStr::Icmp( s, "noOpt" ) ) {
common->Printf( "noOptimize = true\n" );
dmapGlobals.noOptimize = true;
} else if ( !idStr::Icmp( s, "verboseentities" ) ) {
common->Printf( "verboseentities = true\n");
dmapGlobals.verboseentities = true;
} else if ( !idStr::Icmp( s, "noCurves" ) ) {
common->Printf( "noCurves = true\n");
dmapGlobals.noCurves = true;
} else if ( !idStr::Icmp( s, "noModels" ) ) {
common->Printf( "noModels = true\n" );
dmapGlobals.noModelBrushes = true;
} else if ( !idStr::Icmp( s, "noClipSides" ) ) {
common->Printf( "noClipSides = true\n" );
dmapGlobals.noClipSides = true;
} else if ( !idStr::Icmp( s, "noCarve" ) ) {
common->Printf( "noCarve = true\n" );
dmapGlobals.fullCarve = false;
} else if ( !idStr::Icmp( s, "shadowOpt" ) ) {
dmapGlobals.shadowOptLevel = (shadowOptLevel_t)atoi( args.Argv( i+1 ) );
common->Printf( "shadowOpt = %i\n",dmapGlobals.shadowOptLevel );
i += 1;
} else if ( !idStr::Icmp( s, "noTjunc" ) ) {
// triangle optimization won't work properly without tjunction fixing
common->Printf ("noTJunc = true\n" );
dmapGlobals.noTJunc = true;
dmapGlobals.noOptimize = true;
common->Printf ("forcing noOptimize = true\n" );
} else if ( !idStr::Icmp( s, "noCM" ) ) {
noCM = true;
common->Printf( "noCM = true\n" );
} else if ( !idStr::Icmp( s, "noAAS" ) ) {
noAAS = true;
common->Printf( "noAAS = true\n" );
} else if ( !idStr::Icmp( s, "editorOutput" ) ) {
#ifdef _WIN32
com_outputMsg = true;
#endif
} else {
break;
}
}
if ( i >= args.Argc() ) {
common->Error( "usage: dmap [options] mapfile" );
}
passedName = args.Argv(i); // may have an extension
passedName.BackSlashesToSlashes();
if ( passedName.Icmpn( "maps/", 4 ) != 0 ) {
passedName = "maps/" + passedName;
}
idStr stripped = passedName;
stripped.StripFileExtension();
idStr::Copynz( dmapGlobals.mapFileBase, stripped, sizeof(dmapGlobals.mapFileBase) );
bool region = false;
// if this isn't a regioned map, delete the last saved region map
if ( passedName.Right( 4 ) != ".reg" ) {
sprintf( path, "%s.reg", dmapGlobals.mapFileBase );
fileSystem->RemoveFile( path );
} else {
region = true;
}
passedName = stripped;
// delete any old line leak files
sprintf( path, "%s.lin", dmapGlobals.mapFileBase );
fileSystem->RemoveFile( path );
//
// start from scratch
//
start = Sys_Milliseconds();
if ( !LoadDMapFile( passedName ) ) {
return;
}
if ( ProcessModels() ) {
WriteOutputFile();
} else {
leaked = true;
}
FreeDMapFile();
common->Printf( "%i total shadow triangles\n", dmapGlobals.totalShadowTriangles );
common->Printf( "%i total shadow verts\n", dmapGlobals.totalShadowVerts );
end = Sys_Milliseconds();
common->Printf( "-----------------------\n" );
common->Printf( "%5.0f seconds for dmap\n", ( end - start ) * 0.001f );
if ( !leaked ) {
if ( !noCM ) {
// make sure the collision model manager is not used by the game
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
// create the collision map
start = Sys_Milliseconds();
collisionModelManager->LoadMap( dmapGlobals.dmapFile );
collisionModelManager->FreeMap();
end = Sys_Milliseconds();
common->Printf( "-------------------------------------\n" );
common->Printf( "%5.0f seconds to create collision map\n", ( end - start ) * 0.001f );
}
if ( !noAAS && !region ) {
// create AAS files
RunAAS_f( args );
}
}
// free the common .map representation
delete dmapGlobals.dmapFile;
// clear the map plane list
dmapGlobals.mapPlanes.Clear();
#ifdef _WIN32
if ( com_outputMsg && com_hwndMsg != NULL ) {
unsigned int msg = ::RegisterWindowMessage( DMAP_DONE );
::PostMessage( com_hwndMsg, msg, 0, 0 );
}
#endif
}
/*
============
Dmap_f
============
*/
void Dmap_f( const idCmdArgs &args ) {
common->ClearWarnings( "running dmap" );
// refresh the screen each time we print so it doesn't look
// like it is hung
common->SetRefreshOnPrint( true );
Dmap( args );
common->SetRefreshOnPrint( false );
common->PrintWarnings();
}

View File

@@ -0,0 +1,485 @@
/*
===========================================================================
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 "../../../renderer/tr_local.h"
typedef struct primitive_s {
struct primitive_s *next;
// only one of these will be non-NULL
struct bspbrush_s * brush;
struct mapTri_s * tris;
} primitive_t;
typedef struct {
struct optimizeGroup_s *groups;
// we might want to add other fields later
} uArea_t;
typedef struct {
idMapEntity * mapEntity; // points into mapFile_t data
idVec3 origin;
primitive_t * primitives;
struct tree_s * tree;
int numAreas;
uArea_t * areas;
} uEntity_t;
// chains of mapTri_t are the general unit of processing
typedef struct mapTri_s {
struct mapTri_s * next;
const idMaterial * material;
void * mergeGroup; // we want to avoid merging triangles
// from different fixed groups, like guiSurfs and mirrors
int planeNum; // not set universally, just in some areas
idDrawVert v[3];
const struct hashVert_s *hashVert[3];
struct optVertex_s *optVert[3];
} mapTri_t;
typedef struct {
int width, height;
idDrawVert * verts;
} mesh_t;
#define MAX_PATCH_SIZE 32
#define PLANENUM_LEAF -1
typedef struct parseMesh_s {
struct parseMesh_s *next;
mesh_t mesh;
const idMaterial * material;
} parseMesh_t;
typedef struct bspface_s {
struct bspface_s * next;
int planenum;
bool portal; // all portals will be selected before
// any non-portals
bool checked; // used by SelectSplitPlaneNum()
idWinding * w;
} bspface_t;
typedef struct {
idVec4 v[2]; // the offset value will always be in the 0.0 to 1.0 range
} textureVectors_t;
typedef struct side_s {
int planenum;
const idMaterial * material;
textureVectors_t texVec;
idWinding * winding; // only clipped to the other sides of the brush
idWinding * visibleHull; // also clipped to the solid parts of the world
} side_t;
typedef struct bspbrush_s {
struct bspbrush_s * next;
struct bspbrush_s * original; // chopped up brushes will reference the originals
int entitynum; // editor numbering for messages
int brushnum; // editor numbering for messages
const idMaterial * contentShader; // one face's shader will determine the volume attributes
int contents;
bool opaque;
int outputNumber; // set when the brush is written to the file list
idBounds bounds;
int numsides;
side_t sides[6]; // variably sized
} uBrush_t;
typedef struct drawSurfRef_s {
struct drawSurfRef_s * nextRef;
int outputNumber;
} drawSurfRef_t;
typedef struct node_s {
// both leafs and nodes
int planenum; // -1 = leaf node
struct node_s * parent;
idBounds bounds; // valid after portalization
// nodes only
side_t * side; // the side that created the node
struct node_s * children[2];
int nodeNumber; // set after pruning
// leafs only
bool opaque; // view can never be inside
uBrush_t * brushlist; // fragments of all brushes in this leaf
// needed for FindSideForPortal
int area; // determined by flood filling up to areaportals
int occupied; // 1 or greater can reach entity
uEntity_t * occupant; // for leak file testing
struct uPortal_s * portals; // also on nodes during construction
} node_t;
typedef struct uPortal_s {
idPlane plane;
node_t *onnode; // NULL = outside box
node_t *nodes[2]; // [0] = front side of plane
struct uPortal_s *next[2];
idWinding *winding;
} uPortal_t;
// a tree_t is created by FaceBSP()
typedef struct tree_s {
node_t *headnode;
node_t outside_node;
idBounds bounds;
} tree_t;
#define MAX_QPATH 256 // max length of a game pathname
typedef struct {
idRenderLightLocal def;
char name[MAX_QPATH]; // for naming the shadow volume surface and interactions
srfTriangles_t *shadowTris;
} mapLight_t;
#define MAX_GROUP_LIGHTS 16
typedef struct optimizeGroup_s {
struct optimizeGroup_s *nextGroup;
idBounds bounds; // set in CarveGroupsByLight
// all of these must match to add a triangle to the triList
bool smoothed; // curves will never merge with brushes
int planeNum;
int areaNum;
const idMaterial * material;
int numGroupLights;
mapLight_t * groupLights[MAX_GROUP_LIGHTS]; // lights effecting this list
void * mergeGroup; // if this differs (guiSurfs, mirrors, etc), the
// groups will not be combined into model surfaces
// after optimization
textureVectors_t texVec;
bool surfaceEmited;
mapTri_t * triList;
mapTri_t * regeneratedTris; // after each island optimization
idVec3 axis[2]; // orthogonal to the plane, so optimization can be 2D
} optimizeGroup_t;
// all primitives from the map are added to optimzeGroups, creating new ones as needed
// each optimizeGroup is then split into the map areas, creating groups in each area
// each optimizeGroup is then divided by each light, creating more groups
// the final list of groups is then tjunction fixed against all groups, then optimized internally
// multiple optimizeGroups will be merged together into .proc surfaces, but no further optimization
// is done on them
//=============================================================================
// dmap.cpp
typedef enum {
SO_NONE, // 0
SO_MERGE_SURFACES, // 1
SO_CULL_OCCLUDED, // 2
SO_CLIP_OCCLUDERS, // 3
SO_CLIP_SILS, // 4
SO_SIL_OPTIMIZE // 5
} shadowOptLevel_t;
typedef struct {
// mapFileBase will contain the qpath without any extension: "maps/test_box"
char mapFileBase[1024];
idMapFile *dmapFile;
idPlaneSet mapPlanes;
int num_entities;
uEntity_t *uEntities;
int entityNum;
idList<mapLight_t*> mapLights;
bool verbose;
bool glview;
bool noOptimize;
bool verboseentities;
bool noCurves;
bool fullCarve;
bool noModelBrushes;
bool noTJunc;
bool nomerge;
bool noFlood;
bool noClipSides; // don't cut sides by solid leafs, use the entire thing
bool noLightCarve; // extra triangle subdivision by light frustums
shadowOptLevel_t shadowOptLevel;
bool noShadow; // don't create optimized shadow volumes
idBounds drawBounds;
bool drawflag;
int totalShadowTriangles;
int totalShadowVerts;
} dmapGlobals_t;
extern dmapGlobals_t dmapGlobals;
int FindFloatPlane( const idPlane &plane, bool *fixedDegeneracies = NULL );
//=============================================================================
// brush.cpp
#ifndef CLIP_EPSILON
#define CLIP_EPSILON 0.1f
#endif
#define PSIDE_FRONT 1
#define PSIDE_BACK 2
#define PSIDE_BOTH (PSIDE_FRONT|PSIDE_BACK)
#define PSIDE_FACING 4
int CountBrushList (uBrush_t *brushes);
uBrush_t *AllocBrush (int numsides);
void FreeBrush (uBrush_t *brushes);
void FreeBrushList (uBrush_t *brushes);
uBrush_t *CopyBrush (uBrush_t *brush);
void DrawBrushList (uBrush_t *brush);
void PrintBrush (uBrush_t *brush);
bool BoundBrush (uBrush_t *brush);
bool CreateBrushWindings (uBrush_t *brush);
uBrush_t *BrushFromBounds( const idBounds &bounds );
float BrushVolume (uBrush_t *brush);
void WriteBspBrushMap( const char *name, uBrush_t *list );
void FilterBrushesIntoTree( uEntity_t *e );
void SplitBrush( uBrush_t *brush, int planenum, uBrush_t **front, uBrush_t **back);
node_t *AllocNode( void );
//=============================================================================
// map.cpp
bool LoadDMapFile( const char *filename );
void FreeOptimizeGroupList( optimizeGroup_t *groups );
void FreeDMapFile( void );
//=============================================================================
// draw.cpp -- draw debug views either directly, or through glserv.exe
void Draw_ClearWindow( void );
void DrawWinding( const idWinding *w );
void DrawAuxWinding( const idWinding *w );
void DrawLine( idVec3 v1, idVec3 v2, int color );
void GLS_BeginScene( void );
void GLS_Winding( const idWinding *w, int code );
void GLS_Triangle( const mapTri_t *tri, int code );
void GLS_EndScene( void );
//=============================================================================
// portals.cpp
#define MAX_INTER_AREA_PORTALS 1024
typedef struct {
int area0, area1;
side_t *side;
} interAreaPortal_t;
extern interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS];
extern int numInterAreaPortals;
bool FloodEntities( tree_t *tree );
void FillOutside( uEntity_t *e );
void FloodAreas( uEntity_t *e );
void MakeTreePortals( tree_t *tree );
void FreePortal( uPortal_t *p );
//=============================================================================
// glfile.cpp -- write a debug file to be viewd with glview.exe
void OutputWinding( idWinding *w, idFile *glview );
void WriteGLView( tree_t *tree, char *source );
//=============================================================================
// leakfile.cpp
void LeakFile( tree_t *tree );
//=============================================================================
// facebsp.cpp
tree_t *AllocTree( void );
void FreeTree( tree_t *tree );
void FreeTree_r( node_t *node );
void FreeTreePortals_r( node_t *node );
bspface_t *MakeStructuralBspFaceList( primitive_t *list );
bspface_t *MakeVisibleBspFaceList( primitive_t *list );
tree_t *FaceBSP( bspface_t *list );
//=============================================================================
// surface.cpp
mapTri_t *CullTrisInOpaqueLeafs( mapTri_t *triList, tree_t *tree );
void ClipSidesByTree( uEntity_t *e );
void SplitTrisToSurfaces( mapTri_t *triList, tree_t *tree );
void PutPrimitivesInAreas( uEntity_t *e );
void Prelight( uEntity_t *e );
//=============================================================================
// tritjunction.cpp
struct hashVert_s *GetHashVert( idVec3 &v );
void HashTriangles( optimizeGroup_t *groupList );
void FreeTJunctionHash( void );
int CountGroupListTris( const optimizeGroup_t *groupList );
void FixEntityTjunctions( uEntity_t *e );
void FixAreaGroupsTjunctions( optimizeGroup_t *groupList );
void FixGlobalTjunctions( uEntity_t *e );
//=============================================================================
// optimize.cpp -- trianlge mesh reoptimization
// the shadow volume optimizer call internal optimizer routines, normal triangles
// will just be done by OptimizeEntity()
typedef struct optVertex_s {
idDrawVert v;
idVec3 pv; // projected against planar axis, third value is 0
struct optEdge_s *edges;
struct optVertex_s *islandLink;
bool addedToIsland;
bool emited; // when regenerating triangles
} optVertex_t;
typedef struct optEdge_s {
optVertex_t *v1, *v2;
struct optEdge_s *islandLink;
bool addedToIsland;
bool created; // not one of the original edges
bool combined; // combined from two or more colinear edges
struct optTri_s *frontTri, *backTri;
struct optEdge_s *v1link, *v2link;
} optEdge_t;
typedef struct optTri_s {
struct optTri_s *next;
idVec3 midpoint;
optVertex_t *v[3];
bool filled;
} optTri_t;
typedef struct {
optimizeGroup_t *group;
optVertex_t *verts;
optEdge_t *edges;
optTri_t *tris;
} optIsland_t;
void OptimizeEntity( uEntity_t *e );
void OptimizeGroupList( optimizeGroup_t *groupList );
//=============================================================================
// tritools.cpp
mapTri_t *AllocTri( void );
void FreeTri( mapTri_t *tri );
int CountTriList( const mapTri_t *list );
mapTri_t *MergeTriLists( mapTri_t *a, mapTri_t *b );
mapTri_t *CopyTriList( const mapTri_t *a );
void FreeTriList( mapTri_t *a );
mapTri_t *CopyMapTri( const mapTri_t *tri );
float MapTriArea( const mapTri_t *tri );
mapTri_t *RemoveBadTris( const mapTri_t *tri );
void BoundTriList( const mapTri_t *list, idBounds &b );
void DrawTri( const mapTri_t *tri );
void FlipTriList( mapTri_t *tris );
void TriVertsFromOriginal( mapTri_t *tri, const mapTri_t *original );
void PlaneForTri( const mapTri_t *tri, idPlane &plane );
idWinding *WindingForTri( const mapTri_t *tri );
mapTri_t *WindingToTriList( const idWinding *w, const mapTri_t *originalTri );
void ClipTriList( const mapTri_t *list, const idPlane &plane, float epsilon, mapTri_t **front, mapTri_t **back );
//=============================================================================
// output.cpp
srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris );
void WriteOutputFile( void );
//=============================================================================
// shadowopt.cpp
srfTriangles_t *CreateLightShadow( optimizeGroup_t *shadowerGroups, const mapLight_t *light );
void FreeBeamTree( struct beamTree_s *beamTree );
void CarveTriByBeamTree( const struct beamTree_s *beamTree, const mapTri_t *tri, mapTri_t **lit, mapTri_t **unLit );

View File

@@ -0,0 +1,499 @@
/*
===========================================================================
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"
int c_faceLeafs;
extern int c_nodes;
void RemovePortalFromNode( uPortal_t *portal, node_t *l );
node_t *NodeForPoint( node_t *node, idVec3 origin ) {
float d;
while( node->planenum != PLANENUM_LEAF ) {
idPlane &plane = dmapGlobals.mapPlanes[node->planenum];
d = plane.Distance( origin );
if ( d >= 0 ) {
node = node->children[0];
} else {
node = node->children[1];
}
}
return node;
}
/*
=============
FreeTreePortals_r
=============
*/
void FreeTreePortals_r (node_t *node)
{
uPortal_t *p, *nextp;
int s;
// free children
if (node->planenum != PLANENUM_LEAF)
{
FreeTreePortals_r (node->children[0]);
FreeTreePortals_r (node->children[1]);
}
// free portals
for (p=node->portals ; p ; p=nextp)
{
s = (p->nodes[1] == node);
nextp = p->next[s];
RemovePortalFromNode (p, p->nodes[!s]);
FreePortal (p);
}
node->portals = NULL;
}
/*
=============
FreeTree_r
=============
*/
void FreeTree_r (node_t *node)
{
// free children
if (node->planenum != PLANENUM_LEAF)
{
FreeTree_r (node->children[0]);
FreeTree_r (node->children[1]);
}
// free brushes
FreeBrushList (node->brushlist);
// free the node
c_nodes--;
Mem_Free (node);
}
/*
=============
FreeTree
=============
*/
void FreeTree( tree_t *tree ) {
if ( !tree ) {
return;
}
FreeTreePortals_r (tree->headnode);
FreeTree_r (tree->headnode);
Mem_Free (tree);
}
//===============================================================
void PrintTree_r (node_t *node, int depth)
{
int i;
uBrush_t *bb;
for (i=0 ; i<depth ; i++)
common->Printf(" ");
if (node->planenum == PLANENUM_LEAF)
{
if (!node->brushlist)
common->Printf("NULL\n");
else
{
for (bb=node->brushlist ; bb ; bb=bb->next)
common->Printf("%i ", bb->original->brushnum);
common->Printf("\n");
}
return;
}
idPlane &plane = dmapGlobals.mapPlanes[node->planenum];
common->Printf( "#%i (%5.2f %5.2f %5.2f %5.2f)\n", node->planenum,
plane[0], plane[1], plane[2], plane[3] );
PrintTree_r( node->children[0], depth+1 );
PrintTree_r( node->children[1], depth+1 );
}
/*
================
AllocBspFace
================
*/
bspface_t *AllocBspFace( void ) {
bspface_t *f;
f = (bspface_t *)Mem_Alloc(sizeof(*f));
memset( f, 0, sizeof(*f) );
return f;
}
/*
================
FreeBspFace
================
*/
void FreeBspFace( bspface_t *f ) {
if ( f->w ) {
delete f->w;
}
Mem_Free( f );
}
/*
================
SelectSplitPlaneNum
================
*/
#define BLOCK_SIZE 1024
int SelectSplitPlaneNum( node_t *node, bspface_t *list ) {
bspface_t *split;
bspface_t *check;
bspface_t *bestSplit;
int splits, facing, front, back;
int side;
idPlane *mapPlane;
int value, bestValue;
idPlane plane;
int planenum;
bool havePortals;
float dist;
idVec3 halfSize;
// if it is crossing a 1k block boundary, force a split
// this prevents epsilon problems from extending an
// arbitrary distance across the map
halfSize = ( node->bounds[1] - node->bounds[0] ) * 0.5f;
for ( int axis = 0; axis < 3; axis++ ) {
if ( halfSize[axis] > BLOCK_SIZE ) {
dist = BLOCK_SIZE * ( floor( ( node->bounds[0][axis] + halfSize[axis] ) / BLOCK_SIZE ) + 1.0f );
} else {
dist = BLOCK_SIZE * ( floor( node->bounds[0][axis] / BLOCK_SIZE ) + 1.0f );
}
if ( dist > node->bounds[0][axis] + 1.0f && dist < node->bounds[1][axis] - 1.0f ) {
plane[0] = plane[1] = plane[2] = 0.0f;
plane[axis] = 1.0f;
plane[3] = -dist;
planenum = FindFloatPlane( plane );
return planenum;
}
}
// pick one of the face planes
// if we have any portal faces at all, only
// select from them, otherwise select from
// all faces
bestValue = -999999;
bestSplit = list;
havePortals = false;
for ( split = list ; split ; split = split->next ) {
split->checked = false;
if ( split->portal ) {
havePortals = true;
}
}
for ( split = list ; split ; split = split->next ) {
if ( split->checked ) {
continue;
}
if ( havePortals != split->portal ) {
continue;
}
mapPlane = &dmapGlobals.mapPlanes[ split->planenum ];
splits = 0;
facing = 0;
front = 0;
back = 0;
for ( check = list ; check ; check = check->next ) {
if ( check->planenum == split->planenum ) {
facing++;
check->checked = true; // won't need to test this plane again
continue;
}
side = check->w->PlaneSide( *mapPlane );
if ( side == SIDE_CROSS ) {
splits++;
} else if ( side == SIDE_FRONT ) {
front++;
} else if ( side == SIDE_BACK ) {
back++;
}
}
value = 5*facing - 5*splits; // - abs(front-back);
if ( mapPlane->Type() < PLANETYPE_TRUEAXIAL ) {
value+=5; // axial is better
}
if ( value > bestValue ) {
bestValue = value;
bestSplit = split;
}
}
if ( bestValue == -999999 ) {
return -1;
}
return bestSplit->planenum;
}
/*
================
BuildFaceTree_r
================
*/
void BuildFaceTree_r( node_t *node, bspface_t *list ) {
bspface_t *split;
bspface_t *next;
int side;
bspface_t *newFace;
bspface_t *childLists[2];
idWinding *frontWinding, *backWinding;
int i;
int splitPlaneNum;
splitPlaneNum = SelectSplitPlaneNum( node, list );
// if we don't have any more faces, this is a node
if ( splitPlaneNum == -1 ) {
node->planenum = PLANENUM_LEAF;
c_faceLeafs++;
return;
}
// partition the list
node->planenum = splitPlaneNum;
idPlane &plane = dmapGlobals.mapPlanes[ splitPlaneNum ];
childLists[0] = NULL;
childLists[1] = NULL;
for ( split = list ; split ; split = next ) {
next = split->next;
if ( split->planenum == node->planenum ) {
FreeBspFace( split );
continue;
}
side = split->w->PlaneSide( plane );
if ( side == SIDE_CROSS ) {
split->w->Split( plane, CLIP_EPSILON * 2, &frontWinding, &backWinding );
if ( frontWinding ) {
newFace = AllocBspFace();
newFace->w = frontWinding;
newFace->next = childLists[0];
newFace->planenum = split->planenum;
childLists[0] = newFace;
}
if ( backWinding ) {
newFace = AllocBspFace();
newFace->w = backWinding;
newFace->next = childLists[1];
newFace->planenum = split->planenum;
childLists[1] = newFace;
}
FreeBspFace( split );
} else if ( side == SIDE_FRONT ) {
split->next = childLists[0];
childLists[0] = split;
} else if ( side == SIDE_BACK ) {
split->next = childLists[1];
childLists[1] = split;
}
}
// recursively process children
for ( i = 0 ; i < 2 ; i++ ) {
node->children[i] = AllocNode();
node->children[i]->parent = node;
node->children[i]->bounds = node->bounds;
}
// split the bounds if we have a nice axial plane
for ( i = 0 ; i < 3 ; i++ ) {
if ( idMath::Fabs( plane[i] - 1.0 ) < 0.001 ) {
node->children[0]->bounds[0][i] = plane.Dist();
node->children[1]->bounds[1][i] = plane.Dist();
break;
}
}
for ( i = 0 ; i < 2 ; i++ ) {
BuildFaceTree_r ( node->children[i], childLists[i]);
}
}
/*
================
FaceBSP
List will be freed before returning
================
*/
tree_t *FaceBSP( bspface_t *list ) {
tree_t *tree;
bspface_t *face;
int i;
int count;
int start, end;
start = Sys_Milliseconds();
common->Printf( "--- FaceBSP ---\n" );
tree = AllocTree ();
count = 0;
tree->bounds.Clear();
for ( face = list ; face ; face = face->next ) {
count++;
for ( i = 0 ; i < face->w->GetNumPoints() ; i++ ) {
tree->bounds.AddPoint( (*face->w)[i].ToVec3() );
}
}
common->Printf( "%5i faces\n", count );
tree->headnode = AllocNode();
tree->headnode->bounds = tree->bounds;
c_faceLeafs = 0;
BuildFaceTree_r ( tree->headnode, list );
common->Printf( "%5i leafs\n", c_faceLeafs );
end = Sys_Milliseconds();
common->Printf( "%5.1f seconds faceBsp\n", ( end - start ) / 1000.0 );
return tree;
}
//==========================================================================
/*
=================
MakeStructuralBspFaceList
=================
*/
bspface_t *MakeStructuralBspFaceList( primitive_t *list ) {
uBrush_t *b;
int i;
side_t *s;
idWinding *w;
bspface_t *f, *flist;
flist = NULL;
for ( ; list ; list = list->next ) {
b = list->brush;
if ( !b ) {
continue;
}
if ( !b->opaque && !( b->contents & CONTENTS_AREAPORTAL ) ) {
continue;
}
for ( i = 0 ; i < b->numsides ; i++ ) {
s = &b->sides[i];
w = s->winding;
if ( !w ) {
continue;
}
if ( ( b->contents & CONTENTS_AREAPORTAL ) && ! ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
continue;
}
f = AllocBspFace();
if ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) {
f->portal = true;
}
f->w = w->Copy();
f->planenum = s->planenum & ~1;
f->next = flist;
flist = f;
}
}
return flist;
}
/*
=================
MakeVisibleBspFaceList
=================
*/
bspface_t *MakeVisibleBspFaceList( primitive_t *list ) {
uBrush_t *b;
int i;
side_t *s;
idWinding *w;
bspface_t *f, *flist;
flist = NULL;
for ( ; list ; list = list->next ) {
b = list->brush;
if ( !b ) {
continue;
}
if ( !b->opaque && !( b->contents & CONTENTS_AREAPORTAL ) ) {
continue;
}
for ( i = 0 ; i < b->numsides ; i++ ) {
s = &b->sides[i];
w = s->visibleHull;
if ( !w ) {
continue;
}
f = AllocBspFace();
if ( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) {
f->portal = true;
}
f->w = w->Copy();
f->planenum = s->planenum & ~1;
f->next = flist;
flist = f;
}
}
return flist;
}

View File

@@ -0,0 +1,292 @@
/*
===========================================================================
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"
#ifdef WIN32
#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
//#include <GL/glaux.h>
#define WIN_SIZE 1024
void Draw_ClearWindow( void ) {
if ( !dmapGlobals.drawflag ) {
return;
}
glDrawBuffer( GL_FRONT );
RB_SetGL2D();
glClearColor( 0.5, 0.5, 0.5, 0 );
glClear( GL_COLOR_BUFFER_BIT );
#if 0
int w, h, g;
float mx, my;
w = (dmapGlobals.drawBounds.b[1][0] - dmapGlobals.drawBounds.b[0][0]);
h = (dmapGlobals.drawBounds.b[1][1] - dmapGlobals.drawBounds.b[0][1]);
mx = dmapGlobals.drawBounds.b[0][0] + w/2;
my = dmapGlobals.drawBounds.b[1][1] + h/2;
g = w > h ? w : h;
glLoadIdentity ();
gluPerspective (90, 1, 2, 16384);
gluLookAt (mx, my, draw_maxs[2] + g/2, mx , my, draw_maxs[2], 0, 1, 0);
#else
glMatrixMode( GL_PROJECTION );
glLoadIdentity ();
glOrtho( dmapGlobals.drawBounds[0][0], dmapGlobals.drawBounds[1][0],
dmapGlobals.drawBounds[0][1], dmapGlobals.drawBounds[1][1],
-1, 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
#endif
glColor3f (0,0,0);
// glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
glDisable (GL_DEPTH_TEST);
// glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#if 0
//glColor4f (1,0,0,0.5);
// glBegin( GL_LINE_LOOP );
glBegin( GL_QUADS );
glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[0][1] + 20 );
glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[0][1] + 20 );
glVertex2f( dmapGlobals.drawBounds.b[1][0] - 20, dmapGlobals.drawBounds.b[1][1] - 20 );
glVertex2f( dmapGlobals.drawBounds.b[0][0] + 20, dmapGlobals.drawBounds.b[1][1] - 20 );
glEnd ();
#endif
glFlush ();
}
void Draw_SetRed (void)
{
if (!dmapGlobals.drawflag)
return;
glColor3f (1,0,0);
}
void Draw_SetGrey (void)
{
if (!dmapGlobals.drawflag)
return;
glColor3f( 0.5f, 0.5f, 0.5f);
}
void Draw_SetBlack (void)
{
if (!dmapGlobals.drawflag)
return;
glColor3f( 0.0f, 0.0f, 0.0f );
}
void DrawWinding ( const idWinding *w )
{
int i;
if (!dmapGlobals.drawflag)
return;
glColor3f( 0.3f, 0.0f, 0.0f );
glBegin (GL_POLYGON);
for ( i = 0; i < w->GetNumPoints(); i++ )
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
glEnd ();
glColor3f( 1, 0, 0 );
glBegin (GL_LINE_LOOP);
for ( i = 0; i < w->GetNumPoints(); i++ )
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
glEnd ();
glFlush ();
}
void DrawAuxWinding ( const idWinding *w)
{
int i;
if (!dmapGlobals.drawflag)
return;
glColor3f( 0.0f, 0.3f, 0.0f );
glBegin (GL_POLYGON);
for ( i = 0; i < w->GetNumPoints(); i++ )
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
glEnd ();
glColor3f( 0.0f, 1.0f, 0.0f );
glBegin (GL_LINE_LOOP);
for ( i = 0; i < w->GetNumPoints(); i++ )
glVertex3f( (*w)[i][0], (*w)[i][1], (*w)[i][2] );
glEnd ();
glFlush ();
}
void DrawLine( idVec3 v1, idVec3 v2, int color ) {
if (!dmapGlobals.drawflag)
return;
switch( color ) {
case 0: glColor3f( 0, 0, 0 ); break;
case 1: glColor3f( 0, 0, 1 ); break;
case 2: glColor3f( 0, 1, 0 ); break;
case 3: glColor3f( 0, 1, 1 ); break;
case 4: glColor3f( 1, 0, 0 ); break;
case 5: glColor3f( 1, 0, 1 ); break;
case 6: glColor3f( 1, 1, 0 ); break;
case 7: glColor3f( 1, 1, 1 ); break;
}
glBegin( GL_LINES );
glVertex3fv( v1.ToFloatPtr() );
glVertex3fv( v2.ToFloatPtr() );
glEnd();
glFlush();
}
//============================================================
#define GLSERV_PORT 25001
bool wins_init;
int draw_socket;
void GLS_BeginScene (void)
{
WSADATA winsockdata;
WORD wVersionRequested;
struct sockaddr_in address;
int r;
if (!wins_init)
{
wins_init = true;
wVersionRequested = MAKEWORD(1, 1);
r = WSAStartup (MAKEWORD(1, 1), &winsockdata);
if (r)
common->Error( "Winsock initialization failed.");
}
// connect a socket to the server
draw_socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (draw_socket == -1)
common->Error( "draw_socket failed");
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_port = GLSERV_PORT;
r = connect (draw_socket, (struct sockaddr *)&address, sizeof(address));
if (r == -1)
{
closesocket (draw_socket);
draw_socket = 0;
}
}
void GLS_Winding( const idWinding *w, int code )
{
byte buf[1024];
int i, j;
if (!draw_socket)
return;
((int *)buf)[0] = w->GetNumPoints();
((int *)buf)[1] = code;
for ( i = 0; i < w->GetNumPoints(); i++ )
for (j=0 ; j<3 ; j++)
((float *)buf)[2+i*3+j] = (*w)[i][j];
send (draw_socket, (const char *)buf, w->GetNumPoints() * 12 + 8, 0);
}
void GLS_Triangle( const mapTri_t *tri, int code ) {
idWinding w;
w.SetNumPoints( 3 );
VectorCopy( tri->v[0].xyz, w[0] );
VectorCopy( tri->v[1].xyz, w[1] );
VectorCopy( tri->v[2].xyz, w[2] );
GLS_Winding( &w, code );
}
void GLS_EndScene (void)
{
closesocket (draw_socket);
draw_socket = 0;
}
#else
void Draw_ClearWindow( void ) {
}
void DrawWinding( const idWinding *w) {
}
void DrawAuxWinding ( const idWinding *w) {
}
void GLS_Winding( const idWinding *w, int code ) {
}
void GLS_BeginScene (void) {
}
void GLS_EndScene (void)
{
}
#endif

View File

@@ -0,0 +1,158 @@
/*
===========================================================================
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"
int c_glfaces;
int PortalVisibleSides( uPortal_t *p )
{
int fcon, bcon;
if (!p->onnode)
return 0; // outside
fcon = p->nodes[0]->opaque;
bcon = p->nodes[1]->opaque;
// same contents never create a face
if (fcon == bcon)
return 0;
if (!fcon)
return 1;
if (!bcon)
return 2;
return 0;
}
void OutputWinding( idWinding *w, idFile *glview )
{
static int level = 128;
float light;
int i;
glview->WriteFloatString( "%i\n", w->GetNumPoints() );
level += 28;
light = (level&255)/255.0;
for ( i = 0; i < w->GetNumPoints(); i++ ) {
glview->WriteFloatString( "%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f\n",
(*w)[i][0],
(*w)[i][1],
(*w)[i][2],
light,
light,
light );
}
glview->WriteFloatString( "\n" );
}
/*
=============
OutputPortal
=============
*/
void OutputPortal( uPortal_t *p, idFile *glview ) {
idWinding *w;
int sides;
sides = PortalVisibleSides( p );
if ( !sides ) {
return;
}
c_glfaces++;
w = p->winding;
if ( sides == 2 ) { // back side
w = w->Reverse();
}
OutputWinding( w, glview );
if ( sides == 2 ) {
delete w;
}
}
/*
=============
WriteGLView_r
=============
*/
void WriteGLView_r( node_t *node, idFile *glview )
{
uPortal_t *p, *nextp;
if ( node->planenum != PLANENUM_LEAF )
{
WriteGLView_r( node->children[0], glview );
WriteGLView_r( node->children[1], glview );
return;
}
// write all the portals
for ( p = node->portals; p; p = nextp )
{
if ( p->nodes[0] == node )
{
OutputPortal( p, glview );
nextp = p->next[0];
}
else {
nextp = p->next[1];
}
}
}
/*
=============
WriteGLView
=============
*/
void WriteGLView( tree_t *tree, char *source )
{
idFile *glview;
c_glfaces = 0;
common->Printf( "Writing %s\n", source );
glview = fileSystem->OpenExplicitFileWrite( source );
if ( !glview ) {
common->Error( "Couldn't open %s", source );
}
WriteGLView_r( tree->headnode, glview );
fileSystem->CloseFile( glview );
common->Printf( "%5i c_glfaces\n", c_glfaces );
}

View File

@@ -0,0 +1,112 @@
/*
===========================================================================
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"
/*
==============================================================================
LEAF FILE GENERATION
Save out name.line for qe3 to read
==============================================================================
*/
/*
=============
LeakFile
Finds the shortest possible chain of portals
that leads from the outside leaf to a specifically
occupied leaf
=============
*/
void LeakFile (tree_t *tree)
{
idVec3 mid;
FILE *linefile;
idStr filename;
idStr ospath;
node_t *node;
int count;
if (!tree->outside_node.occupied)
return;
common->Printf ("--- LeakFile ---\n");
//
// write the points to the file
//
sprintf( filename, "%s.lin", dmapGlobals.mapFileBase );
ospath = fileSystem->RelativePathToOSPath( filename );
linefile = fopen( ospath, "w" );
if ( !linefile ) {
common->Error( "Couldn't open %s\n", filename.c_str() );
}
count = 0;
node = &tree->outside_node;
while (node->occupied > 1)
{
int next;
uPortal_t *p, *nextportal;
node_t *nextnode;
int s;
// find the best portal exit
next = node->occupied;
for (p=node->portals ; p ; p = p->next[!s])
{
s = (p->nodes[0] == node);
if (p->nodes[s]->occupied
&& p->nodes[s]->occupied < next)
{
nextportal = p;
nextnode = p->nodes[s];
next = nextnode->occupied;
}
}
node = nextnode;
mid = nextportal->winding->GetCenter();
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
count++;
}
// add the occupant center
node->occupant->mapEntity->epairs.GetVector( "origin", "", mid );
fprintf (linefile, "%f %f %f\n", mid[0], mid[1], mid[2]);
common->Printf ("%5i point linefile\n", count+1);
fclose (linefile);
}

View File

@@ -0,0 +1,648 @@
/*
===========================================================================
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 );
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
/*
===========================================================================
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.
===========================================================================
*/
/*
crazy gcc 3.3.5 optimization bug
happens even at -O1
if you remove the 'return NULL;' after Error(), it only happens at -O3 / release
see dmap.gcc.zip test map and .proc outputs
*/
#include "../../../idlib/precompiled.h"
#pragma hdrstop
#include "dmap.h"
extern idBounds optBounds;
#define MAX_OPT_VERTEXES 0x10000
extern int numOptVerts;
extern optVertex_t optVerts[MAX_OPT_VERTEXES];
/*
================
FindOptVertex
================
*/
optVertex_t *FindOptVertex( idDrawVert *v, optimizeGroup_t *opt ) {
int i;
float x, y;
optVertex_t *vert;
// deal with everything strictly as 2D
x = v->xyz * opt->axis[0];
y = v->xyz * opt->axis[1];
// should we match based on the t-junction fixing hash verts?
for ( i = 0 ; i < numOptVerts ; i++ ) {
if ( optVerts[i].pv[0] == x && optVerts[i].pv[1] == y ) {
return &optVerts[i];
}
}
if ( numOptVerts >= MAX_OPT_VERTEXES ) {
common->Error( "MAX_OPT_VERTEXES" );
return NULL;
}
numOptVerts++;
vert = &optVerts[i];
memset( vert, 0, sizeof( *vert ) );
vert->v = *v;
vert->pv[0] = x;
vert->pv[1] = y;
vert->pv[2] = 0;
optBounds.AddPoint( vert->pv );
return vert;
}

View File

@@ -0,0 +1,682 @@
/*
===========================================================================
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"
//=================================================================================
#if 0
should we try and snap values very close to 0.5, 0.25, 0.125, etc?
do we write out normals, or just a "smooth shade" flag?
resolved: normals. otherwise adjacent facet shaded surfaces get their
vertexes merged, and they would have to be split apart before drawing
do we save out "wings" for shadow silhouette info?
#endif
static idFile *procFile;
#define AREANUM_DIFFERENT -2
/*
=============
PruneNodes_r
Any nodes that have all children with the same
area can be combined into a single leaf node
Returns the area number of all children, or
AREANUM_DIFFERENT if not the same.
=============
*/
int PruneNodes_r( node_t *node ) {
int a1, a2;
if ( node->planenum == PLANENUM_LEAF ) {
return node->area;
}
a1 = PruneNodes_r( node->children[0] );
a2 = PruneNodes_r( node->children[1] );
if ( a1 != a2 || a1 == AREANUM_DIFFERENT ) {
return AREANUM_DIFFERENT;
}
// free all the nodes below this point
FreeTreePortals_r( node->children[0] );
FreeTreePortals_r( node->children[1] );
FreeTree_r( node->children[0] );
FreeTree_r( node->children[1] );
// change this node to a leaf
node->planenum = PLANENUM_LEAF;
node->area = a1;
return a1;
}
static void WriteFloat( idFile *f, float v )
{
if ( idMath::Fabs(v - idMath::Rint(v)) < 0.001 ) {
f->WriteFloatString( "%i ", (int)idMath::Rint(v) );
}
else {
f->WriteFloatString( "%f ", v );
}
}
void Write1DMatrix( idFile *f, int x, float *m ) {
int i;
f->WriteFloatString( "( " );
for ( i = 0; i < x; i++ ) {
WriteFloat( f, m[i] );
}
f->WriteFloatString( ") " );
}
static int CountUniqueShaders( optimizeGroup_t *groups ) {
optimizeGroup_t *a, *b;
int count;
count = 0;
for ( a = groups ; a ; a = a->nextGroup ) {
if ( !a->triList ) { // ignore groups with no tris
continue;
}
for ( b = groups ; b != a ; b = b->nextGroup ) {
if ( !b->triList ) {
continue;
}
if ( a->material != b->material ) {
continue;
}
if ( a->mergeGroup != b->mergeGroup ) {
continue;
}
break;
}
if ( a == b ) {
count++;
}
}
return count;
}
/*
==============
MatchVert
==============
*/
#define XYZ_EPSILON 0.01
#define ST_EPSILON 0.001
#define COSINE_EPSILON 0.999
static bool MatchVert( const idDrawVert *a, const idDrawVert *b ) {
if ( idMath::Fabs( a->xyz[0] - b->xyz[0] ) > XYZ_EPSILON ) {
return false;
}
if ( idMath::Fabs( a->xyz[1] - b->xyz[1] ) > XYZ_EPSILON ) {
return false;
}
if ( idMath::Fabs( a->xyz[2] - b->xyz[2] ) > XYZ_EPSILON ) {
return false;
}
if ( idMath::Fabs( a->st[0] - b->st[0] ) > ST_EPSILON ) {
return false;
}
if ( idMath::Fabs( a->st[1] - b->st[1] ) > ST_EPSILON ) {
return false;
}
// if the normal is 0 (smoothed normals), consider it a match
if ( a->normal[0] == 0 && a->normal[1] == 0 && a->normal[2] == 0
&& b->normal[0] == 0 && b->normal[1] == 0 && b->normal[2] == 0 ) {
return true;
}
// otherwise do a dot-product cosine check
if ( DotProduct( a->normal, b->normal ) < COSINE_EPSILON ) {
return false;
}
return true;
}
/*
====================
ShareMapTriVerts
Converts independent triangles to shared vertex triangles
====================
*/
srfTriangles_t *ShareMapTriVerts( const mapTri_t *tris ) {
const mapTri_t *step;
int count;
int i, j;
int numVerts;
int numIndexes;
srfTriangles_t *uTri;
// unique the vertexes
count = CountTriList( tris );
uTri = R_AllocStaticTriSurf();
R_AllocStaticTriSurfVerts( uTri, count * 3 );
R_AllocStaticTriSurfIndexes( uTri, count * 3 );
numVerts = 0;
numIndexes = 0;
for ( step = tris ; step ; step = step->next ) {
for ( i = 0 ; i < 3 ; i++ ) {
const idDrawVert *dv;
dv = &step->v[i];
// search for a match
for ( j = 0 ; j < numVerts ; j++ ) {
if ( MatchVert( &uTri->verts[j], dv ) ) {
break;
}
}
if ( j == numVerts ) {
numVerts++;
uTri->verts[j].xyz = dv->xyz;
uTri->verts[j].normal = dv->normal;
uTri->verts[j].st[0] = dv->st[0];
uTri->verts[j].st[1] = dv->st[1];
}
uTri->indexes[numIndexes++] = j;
}
}
uTri->numVerts = numVerts;
uTri->numIndexes = numIndexes;
return uTri;
}
/*
==================
CleanupUTriangles
==================
*/
static void CleanupUTriangles( srfTriangles_t *tri ) {
// perform cleanup operations
R_RangeCheckIndexes( tri );
R_CreateSilIndexes( tri );
// R_RemoveDuplicatedTriangles( tri ); // this may remove valid overlapped transparent triangles
R_RemoveDegenerateTriangles( tri );
// R_RemoveUnusedVerts( tri );
R_FreeStaticTriSurfSilIndexes( tri );
}
/*
====================
WriteUTriangles
Writes text verts and indexes to procfile
====================
*/
static void WriteUTriangles( const srfTriangles_t *uTris ) {
int col;
int i;
// emit this chain
procFile->WriteFloatString( "/* numVerts = */ %i /* numIndexes = */ %i\n",
uTris->numVerts, uTris->numIndexes );
// verts
col = 0;
for ( i = 0 ; i < uTris->numVerts ; i++ ) {
float vec[8];
const idDrawVert *dv;
dv = &uTris->verts[i];
vec[0] = dv->xyz[0];
vec[1] = dv->xyz[1];
vec[2] = dv->xyz[2];
vec[3] = dv->st[0];
vec[4] = dv->st[1];
vec[5] = dv->normal[0];
vec[6] = dv->normal[1];
vec[7] = dv->normal[2];
Write1DMatrix( procFile, 8, vec );
if ( ++col == 3 ) {
col = 0;
procFile->WriteFloatString( "\n" );
}
}
if ( col != 0 ) {
procFile->WriteFloatString( "\n" );
}
// indexes
col = 0;
for ( i = 0 ; i < uTris->numIndexes ; i++ ) {
procFile->WriteFloatString( "%i ", uTris->indexes[i] );
if ( ++col == 18 ) {
col = 0;
procFile->WriteFloatString( "\n" );
}
}
if ( col != 0 ) {
procFile->WriteFloatString( "\n" );
}
}
/*
====================
WriteShadowTriangles
Writes text verts and indexes to procfile
====================
*/
static void WriteShadowTriangles( const srfTriangles_t *tri ) {
int col;
int i;
// emit this chain
procFile->WriteFloatString( "/* numVerts = */ %i /* noCaps = */ %i /* noFrontCaps = */ %i /* numIndexes = */ %i /* planeBits = */ %i\n",
tri->numVerts, tri->numShadowIndexesNoCaps, tri->numShadowIndexesNoFrontCaps, tri->numIndexes, tri->shadowCapPlaneBits );
// verts
col = 0;
for ( i = 0 ; i < tri->numVerts ; i++ ) {
Write1DMatrix( procFile, 3, &tri->shadowVertexes[i].xyz[0] );
if ( ++col == 5 ) {
col = 0;
procFile->WriteFloatString( "\n" );
}
}
if ( col != 0 ) {
procFile->WriteFloatString( "\n" );
}
// indexes
col = 0;
for ( i = 0 ; i < tri->numIndexes ; i++ ) {
procFile->WriteFloatString( "%i ", tri->indexes[i] );
if ( ++col == 18 ) {
col = 0;
procFile->WriteFloatString( "\n" );
}
}
if ( col != 0 ) {
procFile->WriteFloatString( "\n" );
}
}
/*
=======================
GroupsAreSurfaceCompatible
Planes, texcoords, and groupLights can differ,
but the material and mergegroup must match
=======================
*/
static bool GroupsAreSurfaceCompatible( const optimizeGroup_t *a, const optimizeGroup_t *b ) {
if ( a->material != b->material ) {
return false;
}
if ( a->mergeGroup != b->mergeGroup ) {
return false;
}
return true;
}
/*
====================
WriteOutputSurfaces
====================
*/
static void WriteOutputSurfaces( int entityNum, int areaNum ) {
mapTri_t *ambient, *copy;
int surfaceNum;
int numSurfaces;
idMapEntity *entity;
uArea_t *area;
optimizeGroup_t *group, *groupStep;
int i; // , j;
// int col;
srfTriangles_t *uTri;
// mapTri_t *tri;
typedef struct interactionTris_s {
struct interactionTris_s *next;
mapTri_t *triList;
mapLight_t *light;
} interactionTris_t;
interactionTris_t *interactions, *checkInter; //, *nextInter;
area = &dmapGlobals.uEntities[entityNum].areas[areaNum];
entity = dmapGlobals.uEntities[entityNum].mapEntity;
numSurfaces = CountUniqueShaders( area->groups );
if ( entityNum == 0 ) {
procFile->WriteFloatString( "model { /* name = */ \"_area%i\" /* numSurfaces = */ %i\n\n",
areaNum, numSurfaces );
} else {
const char *name;
entity->epairs.GetString( "name", "", &name );
if ( !name[0] ) {
common->Error( "Entity %i has surfaces, but no name key", entityNum );
}
procFile->WriteFloatString( "model { /* name = */ \"%s\" /* numSurfaces = */ %i\n\n",
name, numSurfaces );
}
surfaceNum = 0;
for ( group = area->groups ; group ; group = group->nextGroup ) {
if ( group->surfaceEmited ) {
continue;
}
// combine all groups compatible with this one
// usually several optimizeGroup_t can be combined into a single
// surface, even though they couldn't be merged together to save
// vertexes because they had different planes, texture coordinates, or lights.
// Different mergeGroups will stay in separate surfaces.
ambient = NULL;
// each light that illuminates any of the groups in the surface will
// get its own list of indexes out of the original surface
interactions = NULL;
for ( groupStep = group ; groupStep ; groupStep = groupStep->nextGroup ) {
if ( groupStep->surfaceEmited ) {
continue;
}
if ( !GroupsAreSurfaceCompatible( group, groupStep ) ) {
continue;
}
// copy it out to the ambient list
copy = CopyTriList( groupStep->triList );
ambient = MergeTriLists( ambient, copy );
groupStep->surfaceEmited = true;
// duplicate it into an interaction for each groupLight
for ( i = 0 ; i < groupStep->numGroupLights ; i++ ) {
for ( checkInter = interactions ; checkInter ; checkInter = checkInter->next ) {
if ( checkInter->light == groupStep->groupLights[i] ) {
break;
}
}
if ( !checkInter ) {
// create a new interaction
checkInter = (interactionTris_t *)Mem_ClearedAlloc( sizeof( *checkInter ) );
checkInter->light = groupStep->groupLights[i];
checkInter->next = interactions;
interactions = checkInter;
}
copy = CopyTriList( groupStep->triList );
checkInter->triList = MergeTriLists( checkInter->triList, copy );
}
}
if ( !ambient ) {
continue;
}
if ( surfaceNum >= numSurfaces ) {
common->Error( "WriteOutputSurfaces: surfaceNum >= numSurfaces" );
}
procFile->WriteFloatString( "/* surface %i */ { ", surfaceNum );
surfaceNum++;
procFile->WriteFloatString( "\"%s\" ", ambient->material->GetName() );
uTri = ShareMapTriVerts( ambient );
FreeTriList( ambient );
CleanupUTriangles( uTri );
WriteUTriangles( uTri );
R_FreeStaticTriSurf( uTri );
procFile->WriteFloatString( "}\n\n" );
}
procFile->WriteFloatString( "}\n\n" );
}
/*
===============
WriteNode_r
===============
*/
static void WriteNode_r( node_t *node ) {
int child[2];
int i;
idPlane *plane;
if ( node->planenum == PLANENUM_LEAF ) {
// we shouldn't get here unless the entire world
// was a single leaf
procFile->WriteFloatString( "/* node 0 */ ( 0 0 0 0 ) -1 -1\n" );
return;
}
for ( i = 0 ; i < 2 ; i++ ) {
if ( node->children[i]->planenum == PLANENUM_LEAF ) {
child[i] = -1 - node->children[i]->area;
} else {
child[i] = node->children[i]->nodeNumber;
}
}
plane = &dmapGlobals.mapPlanes[node->planenum];
procFile->WriteFloatString( "/* node %i */ ", node->nodeNumber );
Write1DMatrix( procFile, 4, plane->ToFloatPtr() );
procFile->WriteFloatString( "%i %i\n", child[0], child[1] );
if ( child[0] > 0 ) {
WriteNode_r( node->children[0] );
}
if ( child[1] > 0 ) {
WriteNode_r( node->children[1] );
}
}
static int NumberNodes_r( node_t *node, int nextNumber ) {
if ( node->planenum == PLANENUM_LEAF ) {
return nextNumber;
}
node->nodeNumber = nextNumber;
nextNumber++;
nextNumber = NumberNodes_r( node->children[0], nextNumber );
nextNumber = NumberNodes_r( node->children[1], nextNumber );
return nextNumber;
}
/*
====================
WriteOutputNodes
====================
*/
static void WriteOutputNodes( node_t *node ) {
int numNodes;
// prune unneeded nodes and count
PruneNodes_r( node );
numNodes = NumberNodes_r( node, 0 );
// output
procFile->WriteFloatString( "nodes { /* numNodes = */ %i\n\n", numNodes );
procFile->WriteFloatString( "/* node format is: ( planeVector ) positiveChild negativeChild */\n" );
procFile->WriteFloatString( "/* a child number of 0 is an opaque, solid area */\n" );
procFile->WriteFloatString( "/* negative child numbers are areas: (-1-child) */\n" );
WriteNode_r( node );
procFile->WriteFloatString( "}\n\n" );
}
/*
====================
WriteOutputPortals
====================
*/
static void WriteOutputPortals( uEntity_t *e ) {
int i, j;
interAreaPortal_t *iap;
idWinding *w;
procFile->WriteFloatString( "interAreaPortals { /* numAreas = */ %i /* numIAP = */ %i\n\n",
e->numAreas, numInterAreaPortals );
procFile->WriteFloatString( "/* interAreaPortal format is: numPoints positiveSideArea negativeSideArea ( point) ... */\n" );
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
iap = &interAreaPortals[i];
w = iap->side->winding;
procFile->WriteFloatString("/* iap %i */ %i %i %i ", i, w->GetNumPoints(), iap->area0, iap->area1 );
for ( j = 0 ; j < w->GetNumPoints() ; j++ ) {
Write1DMatrix( procFile, 3, (*w)[j].ToFloatPtr() );
}
procFile->WriteFloatString("\n" );
}
procFile->WriteFloatString( "}\n\n" );
}
/*
====================
WriteOutputEntity
====================
*/
static void WriteOutputEntity( int entityNum ) {
int i;
uEntity_t *e;
e = &dmapGlobals.uEntities[entityNum];
if ( entityNum != 0 ) {
// entities may have enclosed, empty areas that we don't need to write out
if ( e->numAreas > 1 ) {
e->numAreas = 1;
}
}
for ( i = 0 ; i < e->numAreas ; i++ ) {
WriteOutputSurfaces( entityNum, i );
}
// we will completely skip the portals and nodes if it is a single area
if ( entityNum == 0 && e->numAreas > 1 ) {
// output the area portals
WriteOutputPortals( e );
// output the nodes
WriteOutputNodes( e->tree->headnode );
}
}
/*
====================
WriteOutputFile
====================
*/
void WriteOutputFile( void ) {
int i;
uEntity_t *entity;
idStr qpath;
// write the file
common->Printf( "----- WriteOutputFile -----\n" );
sprintf( qpath, "%s." PROC_FILE_EXT, dmapGlobals.mapFileBase );
common->Printf( "writing %s\n", qpath.c_str() );
// _D3XP used fs_cdpath
procFile = fileSystem->OpenFileWrite( qpath, "fs_devpath" );
if ( !procFile ) {
common->Error( "Error opening %s", qpath.c_str() );
}
procFile->WriteFloatString( "%s\n\n", PROC_FILE_ID );
// write the entity models and information, writing entities first
for ( i=dmapGlobals.num_entities - 1 ; i >= 0 ; i-- ) {
entity = &dmapGlobals.uEntities[i];
if ( !entity->primitives ) {
continue;
}
WriteOutputEntity( i );
}
// write the shadow volumes
for ( i = 0 ; i < dmapGlobals.mapLights.Num() ; i++ ) {
mapLight_t *light = dmapGlobals.mapLights[i];
if ( !light->shadowTris ) {
continue;
}
procFile->WriteFloatString( "shadowModel { /* name = */ \"_prelight_%s\"\n\n", light->name );
WriteShadowTriangles( light->shadowTris );
procFile->WriteFloatString( "}\n\n" );
R_FreeStaticTriSurf( light->shadowTris );
light->shadowTris = NULL;
}
fileSystem->CloseFile( procFile );
}

View File

@@ -0,0 +1,999 @@
/*
===========================================================================
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"
interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS];
int numInterAreaPortals;
int c_active_portals;
int c_peak_portals;
/*
===========
AllocPortal
===========
*/
uPortal_t *AllocPortal (void)
{
uPortal_t *p;
c_active_portals++;
if (c_active_portals > c_peak_portals)
c_peak_portals = c_active_portals;
p = (uPortal_t *)Mem_Alloc (sizeof(uPortal_t ));
memset (p, 0, sizeof(uPortal_t ));
return p;
}
void FreePortal (uPortal_t *p)
{
if (p->winding)
delete p->winding;
c_active_portals--;
Mem_Free (p);
}
//==============================================================
/*
=============
Portal_Passable
Returns true if the portal has non-opaque leafs on both sides
=============
*/
static bool Portal_Passable( uPortal_t *p ) {
if (!p->onnode) {
return false; // to global outsideleaf
}
if (p->nodes[0]->planenum != PLANENUM_LEAF
|| p->nodes[1]->planenum != PLANENUM_LEAF) {
common->Error( "Portal_EntityFlood: not a leaf");
}
if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) {
return true;
}
return false;
}
//=============================================================================
int c_tinyportals;
/*
=============
AddPortalToNodes
=============
*/
void AddPortalToNodes (uPortal_t *p, node_t *front, node_t *back) {
if (p->nodes[0] || p->nodes[1]) {
common->Error( "AddPortalToNode: allready included");
}
p->nodes[0] = front;
p->next[0] = front->portals;
front->portals = p;
p->nodes[1] = back;
p->next[1] = back->portals;
back->portals = p;
}
/*
=============
RemovePortalFromNode
=============
*/
void RemovePortalFromNode (uPortal_t *portal, node_t *l)
{
uPortal_t **pp, *t;
// remove reference to the current portal
pp = &l->portals;
while (1)
{
t = *pp;
if (!t)
common->Error( "RemovePortalFromNode: portal not in leaf");
if ( t == portal )
break;
if (t->nodes[0] == l)
pp = &t->next[0];
else if (t->nodes[1] == l)
pp = &t->next[1];
else
common->Error( "RemovePortalFromNode: portal not bounding leaf");
}
if ( portal->nodes[0] == l ) {
*pp = portal->next[0];
portal->nodes[0] = NULL;
} else if ( portal->nodes[1] == l ) {
*pp = portal->next[1];
portal->nodes[1] = NULL;
} else {
common->Error( "RemovePortalFromNode: mislinked" );
}
}
//============================================================================
void PrintPortal (uPortal_t *p)
{
int i;
idWinding *w;
w = p->winding;
for ( i = 0; i < w->GetNumPoints(); i++ )
common->Printf("(%5.0f,%5.0f,%5.0f)\n",(*w)[i][0], (*w)[i][1], (*w)[i][2]);
}
/*
================
MakeHeadnodePortals
The created portals will face the global outside_node
================
*/
#define SIDESPACE 8
static void MakeHeadnodePortals( tree_t *tree ) {
idBounds bounds;
int i, j, n;
uPortal_t *p, *portals[6];
idPlane bplanes[6], *pl;
node_t *node;
node = tree->headnode;
tree->outside_node.planenum = PLANENUM_LEAF;
tree->outside_node.brushlist = NULL;
tree->outside_node.portals = NULL;
tree->outside_node.opaque = false;
// if no nodes, don't go any farther
if ( node->planenum == PLANENUM_LEAF ) {
return;
}
// pad with some space so there will never be null volume leafs
for (i=0 ; i<3 ; i++) {
bounds[0][i] = tree->bounds[0][i] - SIDESPACE;
bounds[1][i] = tree->bounds[1][i] + SIDESPACE;
if ( bounds[0][i] >= bounds[1][i] ) {
common->Error( "Backwards tree volume" );
}
}
for (i=0 ; i<3 ; i++) {
for (j=0 ; j<2 ; j++) {
n = j*3 + i;
p = AllocPortal ();
portals[n] = p;
pl = &bplanes[n];
memset (pl, 0, sizeof(*pl));
if (j) {
(*pl)[i] = -1;
(*pl)[3] = bounds[j][i];
} else {
(*pl)[i] = 1;
(*pl)[3] = -bounds[j][i];
}
p->plane = *pl;
p->winding = new idWinding( *pl );
AddPortalToNodes (p, node, &tree->outside_node);
}
}
// clip the basewindings by all the other planes
for (i=0 ; i<6 ; i++) {
for (j=0 ; j<6 ; j++) {
if (j == i) {
continue;
}
portals[i]->winding = portals[i]->winding->Clip( bplanes[j], ON_EPSILON );
}
}
}
//===================================================
/*
================
BaseWindingForNode
================
*/
#define BASE_WINDING_EPSILON 0.001f
#define SPLIT_WINDING_EPSILON 0.001f
idWinding *BaseWindingForNode (node_t *node) {
idWinding *w;
node_t *n;
w = new idWinding( dmapGlobals.mapPlanes[node->planenum] );
// clip by all the parents
for ( n = node->parent ; n && w ; ) {
idPlane &plane = dmapGlobals.mapPlanes[n->planenum];
if ( n->children[0] == node ) {
// take front
w = w->Clip( plane, BASE_WINDING_EPSILON );
} else {
// take back
idPlane back = -plane;
w = w->Clip( back, BASE_WINDING_EPSILON );
}
node = n;
n = n->parent;
}
return w;
}
//============================================================
/*
==================
MakeNodePortal
create the new portal by taking the full plane winding for the cutting plane
and clipping it by all of parents of this node
==================
*/
static void MakeNodePortal( node_t *node ) {
uPortal_t *new_portal, *p;
idWinding *w;
idVec3 normal;
int side;
w = BaseWindingForNode (node);
// clip the portal by all the other portals in the node
for (p = node->portals ; p && w; p = p->next[side])
{
idPlane plane;
if (p->nodes[0] == node)
{
side = 0;
plane = p->plane;
}
else if (p->nodes[1] == node)
{
side = 1;
plane = -p->plane;
}
else {
common->Error( "CutNodePortals_r: mislinked portal");
side = 0; // quiet a compiler warning
}
w = w->Clip( plane, CLIP_EPSILON );
}
if (!w)
{
return;
}
if ( w->IsTiny() )
{
c_tinyportals++;
delete w;
return;
}
new_portal = AllocPortal ();
new_portal->plane = dmapGlobals.mapPlanes[node->planenum];
new_portal->onnode = node;
new_portal->winding = w;
AddPortalToNodes (new_portal, node->children[0], node->children[1]);
}
/*
==============
SplitNodePortals
Move or split the portals that bound node so that the node's
children have portals instead of node.
==============
*/
static void SplitNodePortals( node_t *node ) {
uPortal_t *p, *next_portal, *new_portal;
node_t *f, *b, *other_node;
int side;
idPlane *plane;
idWinding *frontwinding, *backwinding;
plane = &dmapGlobals.mapPlanes[node->planenum];
f = node->children[0];
b = node->children[1];
for ( p = node->portals ; p ; p = next_portal ) {
if (p->nodes[0] == node ) {
side = 0;
} else if ( p->nodes[1] == node ) {
side = 1;
} else {
common->Error( "SplitNodePortals: mislinked portal" );
side = 0; // quiet a compiler warning
}
next_portal = p->next[side];
other_node = p->nodes[!side];
RemovePortalFromNode (p, p->nodes[0]);
RemovePortalFromNode (p, p->nodes[1]);
//
// cut the portal into two portals, one on each side of the cut plane
//
p->winding->Split( *plane, SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
if ( frontwinding && frontwinding->IsTiny() )
{
delete frontwinding;
frontwinding = NULL;
c_tinyportals++;
}
if ( backwinding && backwinding->IsTiny() )
{
delete backwinding;
backwinding = NULL;
c_tinyportals++;
}
if ( !frontwinding && !backwinding )
{ // tiny windings on both sides
continue;
}
if (!frontwinding)
{
delete backwinding;
if (side == 0)
AddPortalToNodes (p, b, other_node);
else
AddPortalToNodes (p, other_node, b);
continue;
}
if (!backwinding)
{
delete frontwinding;
if (side == 0)
AddPortalToNodes (p, f, other_node);
else
AddPortalToNodes (p, other_node, f);
continue;
}
// the winding is split
new_portal = AllocPortal ();
*new_portal = *p;
new_portal->winding = backwinding;
delete p->winding;
p->winding = frontwinding;
if (side == 0)
{
AddPortalToNodes (p, f, other_node);
AddPortalToNodes (new_portal, b, other_node);
}
else
{
AddPortalToNodes (p, other_node, f);
AddPortalToNodes (new_portal, other_node, b);
}
}
node->portals = NULL;
}
/*
================
CalcNodeBounds
================
*/
void CalcNodeBounds (node_t *node)
{
uPortal_t *p;
int s;
int i;
// calc mins/maxs for both leafs and nodes
node->bounds.Clear();
for (p = node->portals ; p ; p = p->next[s]) {
s = (p->nodes[1] == node);
for ( i = 0; i < p->winding->GetNumPoints(); i++ ) {
node->bounds.AddPoint( (*p->winding)[i].ToVec3() );
}
}
}
/*
==================
MakeTreePortals_r
==================
*/
void MakeTreePortals_r (node_t *node)
{
int i;
CalcNodeBounds( node );
if ( node->bounds[0][0] >= node->bounds[1][0]) {
common->Warning( "node without a volume" );
}
for ( i = 0; i < 3; i++ ) {
if ( node->bounds[0][i] < MIN_WORLD_COORD || node->bounds[1][i] > MAX_WORLD_COORD ) {
common->Warning( "node with unbounded volume");
break;
}
}
if ( node->planenum == PLANENUM_LEAF ) {
return;
}
MakeNodePortal (node);
SplitNodePortals (node);
MakeTreePortals_r (node->children[0]);
MakeTreePortals_r (node->children[1]);
}
/*
==================
MakeTreePortals
==================
*/
void MakeTreePortals (tree_t *tree)
{
common->Printf( "----- MakeTreePortals -----\n");
MakeHeadnodePortals (tree);
MakeTreePortals_r (tree->headnode);
}
/*
=========================================================
FLOOD ENTITIES
=========================================================
*/
int c_floodedleafs;
/*
=============
FloodPortals_r
=============
*/
void FloodPortals_r (node_t *node, int dist) {
uPortal_t *p;
int s;
if ( node->occupied ) {
return;
}
if ( node->opaque ) {
return;
}
c_floodedleafs++;
node->occupied = dist;
for (p=node->portals ; p ; p = p->next[s]) {
s = (p->nodes[1] == node);
FloodPortals_r (p->nodes[!s], dist+1);
}
}
/*
=============
PlaceOccupant
=============
*/
bool PlaceOccupant( node_t *headnode, idVec3 origin, uEntity_t *occupant ) {
node_t *node;
float d;
idPlane *plane;
// find the leaf to start in
node = headnode;
while ( node->planenum != PLANENUM_LEAF ) {
plane = &dmapGlobals.mapPlanes[node->planenum];
d = plane->Distance( origin );
if ( d >= 0.0f ) {
node = node->children[0];
} else {
node = node->children[1];
}
}
if ( node->opaque ) {
return false;
}
node->occupant = occupant;
FloodPortals_r (node, 1);
return true;
}
/*
=============
FloodEntities
Marks all nodes that can be reached by entites
=============
*/
bool FloodEntities( tree_t *tree ) {
int i;
idVec3 origin;
const char *cl;
bool inside;
node_t *headnode;
headnode = tree->headnode;
common->Printf ("--- FloodEntities ---\n");
inside = false;
tree->outside_node.occupied = 0;
c_floodedleafs = 0;
bool errorShown = false;
for (i=1 ; i<dmapGlobals.num_entities ; i++) {
idMapEntity *mapEnt;
mapEnt = dmapGlobals.uEntities[i].mapEntity;
if ( !mapEnt->epairs.GetVector( "origin", "", origin) ) {
continue;
}
// any entity can have "noFlood" set to skip it
if ( mapEnt->epairs.GetString( "noFlood", "", &cl ) ) {
continue;
}
mapEnt->epairs.GetString( "classname", "", &cl );
if ( !strcmp( cl, "light" ) ) {
const char *v;
// don't place lights that have a light_start field, because they can still
// be valid if their origin is outside the world
mapEnt->epairs.GetString( "light_start", "", &v);
if ( v[0] ) {
continue;
}
// don't place fog lights, because they often
// have origins outside the light
mapEnt->epairs.GetString( "texture", "", &v);
if ( v[0] ) {
const idMaterial *mat = declManager->FindMaterial( v );
if ( mat->IsFogLight() ) {
continue;
}
}
}
if (PlaceOccupant (headnode, origin, &dmapGlobals.uEntities[i])) {
inside = true;
}
if (tree->outside_node.occupied && !errorShown) {
errorShown = true;
common->Printf("Leak on entity # %d\n", i);
const char *p;
mapEnt->epairs.GetString( "classname", "", &p);
common->Printf("Entity classname was: %s\n", p);
mapEnt->epairs.GetString( "name", "", &p);
common->Printf("Entity name was: %s\n", p);
idVec3 origin;
if ( mapEnt->epairs.GetVector( "origin", "", origin)) {
common->Printf("Entity origin is: %f %f %f\n\n\n", origin.x, origin.y, origin.z);
}
}
}
common->Printf("%5i flooded leafs\n", c_floodedleafs );
if (!inside)
{
common->Printf ("no entities in open -- no filling\n");
}
else if (tree->outside_node.occupied)
{
common->Printf ("entity reached from outside -- no filling\n");
}
return (bool)(inside && !tree->outside_node.occupied);
}
/*
=========================================================
FLOOD AREAS
=========================================================
*/
static int c_areas;
static int c_areaFloods;
/*
=================
FindSideForPortal
=================
*/
static side_t *FindSideForPortal( uPortal_t *p ) {
int i, j, k;
node_t *node;
uBrush_t *b, *orig;
side_t *s, *s2;
// scan both bordering nodes brush lists for a portal brush
// that shares the plane
for ( i = 0 ; i < 2 ; i++ ) {
node = p->nodes[i];
for ( b = node->brushlist ; b ; b = b->next ) {
if ( !( b->contents & CONTENTS_AREAPORTAL ) ) {
continue;
}
orig = b->original;
for ( j = 0 ; j < orig->numsides ; j++ ) {
s = orig->sides + j;
if ( !s->visibleHull ) {
continue;
}
if ( !( s->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
continue;
}
if ( ( s->planenum & ~1 ) != ( p->onnode->planenum & ~1 ) ) {
continue;
}
// remove the visible hull from any other portal sides of this portal brush
for ( k = 0; k < orig->numsides; k++ ) {
if ( k == j ) {
continue;
}
s2 = orig->sides + k;
if ( s2->visibleHull == NULL ) {
continue;
}
if ( !( s2->material->GetContentFlags() & CONTENTS_AREAPORTAL ) ) {
continue;
}
common->Warning( "brush has multiple area portal sides at %s", s2->visibleHull->GetCenter().ToString() );
delete s2->visibleHull;
s2->visibleHull = NULL;
}
return s;
}
}
}
return NULL;
}
/*
=============
FloodAreas_r
=============
*/
void FloodAreas_r (node_t *node)
{
uPortal_t *p;
int s;
if ( node->area != -1 ) {
return; // allready got it
}
if ( node->opaque ) {
return;
}
c_areaFloods++;
node->area = c_areas;
for ( p=node->portals ; p ; p = p->next[s] ) {
node_t *other;
s = (p->nodes[1] == node);
other = p->nodes[!s];
if ( !Portal_Passable(p) ) {
continue;
}
// can't flood through an area portal
if ( FindSideForPortal( p ) ) {
continue;
}
FloodAreas_r( other );
}
}
/*
=============
FindAreas_r
Just decend the tree, and for each node that hasn't had an
area set, flood fill out from there
=============
*/
void FindAreas_r( node_t *node ) {
if ( node->planenum != PLANENUM_LEAF ) {
FindAreas_r (node->children[0]);
FindAreas_r (node->children[1]);
return;
}
if ( node->opaque ) {
return;
}
if ( node->area != -1 ) {
return; // allready got it
}
c_areaFloods = 0;
FloodAreas_r (node);
common->Printf( "area %i has %i leafs\n", c_areas, c_areaFloods );
c_areas++;
}
/*
============
CheckAreas_r
============
*/
void CheckAreas_r( node_t *node ) {
if ( node->planenum != PLANENUM_LEAF ) {
CheckAreas_r (node->children[0]);
CheckAreas_r (node->children[1]);
return;
}
if ( !node->opaque && node->area < 0 ) {
common->Error( "CheckAreas_r: area = %i", node->area );
}
}
/*
============
ClearAreas_r
Set all the areas to -1 before filling
============
*/
void ClearAreas_r( node_t *node ) {
if ( node->planenum != PLANENUM_LEAF ) {
ClearAreas_r (node->children[0]);
ClearAreas_r (node->children[1]);
return;
}
node->area = -1;
}
//=============================================================
/*
=================
FindInterAreaPortals_r
=================
*/
static void FindInterAreaPortals_r( node_t *node ) {
uPortal_t *p;
int s;
int i;
idWinding *w;
interAreaPortal_t *iap;
side_t *side;
if ( node->planenum != PLANENUM_LEAF ) {
FindInterAreaPortals_r( node->children[0] );
FindInterAreaPortals_r( node->children[1] );
return;
}
if ( node->opaque ) {
return;
}
for ( p=node->portals ; p ; p = p->next[s] ) {
node_t *other;
s = (p->nodes[1] == node);
other = p->nodes[!s];
if ( other->opaque ) {
continue;
}
// only report areas going from lower number to higher number
// so we don't report the portal twice
if ( other->area <= node->area ) {
continue;
}
side = FindSideForPortal( p );
// w = p->winding;
if ( !side ) {
common->Warning( "FindSideForPortal failed at %s", p->winding->GetCenter().ToString() );
continue;
}
w = side->visibleHull;
if ( !w ) {
continue;
}
// see if we have created this portal before
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
iap = &interAreaPortals[i];
if ( side == iap->side &&
( ( p->nodes[0]->area == iap->area0 && p->nodes[1]->area == iap->area1 )
|| ( p->nodes[1]->area == iap->area0 && p->nodes[0]->area == iap->area1 ) ) ) {
break;
}
}
if ( i != numInterAreaPortals ) {
continue; // already emited
}
iap = &interAreaPortals[numInterAreaPortals];
numInterAreaPortals++;
if ( side->planenum == p->onnode->planenum ) {
iap->area0 = p->nodes[0]->area;
iap->area1 = p->nodes[1]->area;
} else {
iap->area0 = p->nodes[1]->area;
iap->area1 = p->nodes[0]->area;
}
iap->side = side;
}
}
/*
=============
FloodAreas
Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
Sets e->areas.numAreas
=============
*/
void FloodAreas( uEntity_t *e ) {
common->Printf ("--- FloodAreas ---\n");
// set all areas to -1
ClearAreas_r( e->tree->headnode );
// flood fill from non-opaque areas
c_areas = 0;
FindAreas_r( e->tree->headnode );
common->Printf ("%5i areas\n", c_areas);
e->numAreas = c_areas;
// make sure we got all of them
CheckAreas_r( e->tree->headnode );
// identify all portals between areas if this is the world
if ( e == &dmapGlobals.uEntities[0] ) {
numInterAreaPortals = 0;
FindInterAreaPortals_r( e->tree->headnode );
}
}
/*
======================================================
FILL OUTSIDE
======================================================
*/
static int c_outside;
static int c_inside;
static int c_solid;
void FillOutside_r (node_t *node)
{
if (node->planenum != PLANENUM_LEAF)
{
FillOutside_r (node->children[0]);
FillOutside_r (node->children[1]);
return;
}
// anything not reachable by an entity
// can be filled away
if (!node->occupied) {
if ( !node->opaque ) {
c_outside++;
node->opaque = true;
} else {
c_solid++;
}
} else {
c_inside++;
}
}
/*
=============
FillOutside
Fill (set node->opaque = true) all nodes that can't be reached by entities
=============
*/
void FillOutside( uEntity_t *e ) {
c_outside = 0;
c_inside = 0;
c_solid = 0;
common->Printf ("--- FillOutside ---\n");
FillOutside_r( e->tree->headnode );
common->Printf ("%5i solid leafs\n", c_solid);
common->Printf ("%5i leafs filled\n", c_outside);
common->Printf ("%5i inside leafs\n", c_inside);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,663 @@
/*
===========================================================================
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"
/*
T junction fixing never creates more xyz points, but
new vertexes will be created when different surfaces
cause a fix
The vertex cleaning accomplishes two goals: removing extranious low order
bits to avoid numbers like 1.000001233, and grouping nearby vertexes
together. Straight truncation accomplishes the first foal, but two vertexes
only a tiny epsilon apart could still be spread to different snap points.
To avoid this, we allow the merge test to group points together that
snapped to neighboring integer coordinates.
Snaping verts can drag some triangles backwards or collapse them to points,
which will cause them to be removed.
When snapping to ints, a point can move a maximum of sqrt(3)/2 distance
Two points that were an epsilon apart can then become sqrt(3) apart
A case that causes recursive overflow with point to triangle fixing:
A
C D
B
Triangle ABC tests against point D and splits into triangles ADC and DBC
Triangle DBC then tests against point A again and splits into ABC and ADB
infinite recursive loop
For a given source triangle
init the no-check list to hold the three triangle hashVerts
recursiveFixTriAgainstHash
recursiveFixTriAgainstHashVert_r
if hashVert is on the no-check list
exit
if the hashVert should split the triangle
add to the no-check list
recursiveFixTriAgainstHash(a)
recursiveFixTriAgainstHash(b)
*/
#define SNAP_FRACTIONS 32
//#define SNAP_FRACTIONS 8
//#define SNAP_FRACTIONS 1
#define VERTEX_EPSILON ( 1.0 / SNAP_FRACTIONS )
#define COLINEAR_EPSILON ( 1.8 * VERTEX_EPSILON )
#define HASH_BINS 16
typedef struct hashVert_s {
struct hashVert_s *next;
idVec3 v;
int iv[3];
} hashVert_t;
static idBounds hashBounds;
static idVec3 hashScale;
static hashVert_t *hashVerts[HASH_BINS][HASH_BINS][HASH_BINS];
static int numHashVerts, numTotalVerts;
static int hashIntMins[3], hashIntScale[3];
/*
===============
GetHashVert
Also modifies the original vert to the snapped value
===============
*/
struct hashVert_s *GetHashVert( idVec3 &v ) {
int iv[3];
int block[3];
int i;
hashVert_t *hv;
numTotalVerts++;
// snap the vert to integral values
for ( i = 0 ; i < 3 ; i++ ) {
iv[i] = floor( ( v[i] + 0.5/SNAP_FRACTIONS ) * SNAP_FRACTIONS );
block[i] = ( iv[i] - hashIntMins[i] ) / hashIntScale[i];
if ( block[i] < 0 ) {
block[i] = 0;
} else if ( block[i] >= HASH_BINS ) {
block[i] = HASH_BINS - 1;
}
}
// see if a vertex near enough already exists
// this could still fail to find a near neighbor right at the hash block boundary
for ( hv = hashVerts[block[0]][block[1]][block[2]] ; hv ; hv = hv->next ) {
#if 0
if ( hv->iv[0] == iv[0] && hv->iv[1] == iv[1] && hv->iv[2] == iv[2] ) {
VectorCopy( hv->v, v );
return hv;
}
#else
for ( i = 0 ; i < 3 ; i++ ) {
int d;
d = hv->iv[i] - iv[i];
if ( d < -1 || d > 1 ) {
break;
}
}
if ( i == 3 ) {
VectorCopy( hv->v, v );
return hv;
}
#endif
}
// create a new one
hv = (hashVert_t *)Mem_Alloc( sizeof( *hv ) );
hv->next = hashVerts[block[0]][block[1]][block[2]];
hashVerts[block[0]][block[1]][block[2]] = hv;
hv->iv[0] = iv[0];
hv->iv[1] = iv[1];
hv->iv[2] = iv[2];
hv->v[0] = (float)iv[0] / SNAP_FRACTIONS;
hv->v[1] = (float)iv[1] / SNAP_FRACTIONS;
hv->v[2] = (float)iv[2] / SNAP_FRACTIONS;
VectorCopy( hv->v, v );
numHashVerts++;
return hv;
}
/*
==================
HashBlocksForTri
Returns an inclusive bounding box of hash
bins that should hold the triangle
==================
*/
static void HashBlocksForTri( const mapTri_t *tri, int blocks[2][3] ) {
idBounds bounds;
int i;
bounds.Clear();
bounds.AddPoint( tri->v[0].xyz );
bounds.AddPoint( tri->v[1].xyz );
bounds.AddPoint( tri->v[2].xyz );
// add a 1.0 slop margin on each side
for ( i = 0 ; i < 3 ; i++ ) {
blocks[0][i] = ( bounds[0][i] - 1.0 - hashBounds[0][i] ) / hashScale[i];
if ( blocks[0][i] < 0 ) {
blocks[0][i] = 0;
} else if ( blocks[0][i] >= HASH_BINS ) {
blocks[0][i] = HASH_BINS - 1;
}
blocks[1][i] = ( bounds[1][i] + 1.0 - hashBounds[0][i] ) / hashScale[i];
if ( blocks[1][i] < 0 ) {
blocks[1][i] = 0;
} else if ( blocks[1][i] >= HASH_BINS ) {
blocks[1][i] = HASH_BINS - 1;
}
}
}
/*
=================
HashTriangles
Removes triangles that are degenerated or flipped backwards
=================
*/
void HashTriangles( optimizeGroup_t *groupList ) {
mapTri_t *a;
int vert;
int i;
optimizeGroup_t *group;
// clear the hash tables
memset( hashVerts, 0, sizeof( hashVerts ) );
numHashVerts = 0;
numTotalVerts = 0;
// bound all the triangles to determine the bucket size
hashBounds.Clear();
for ( group = groupList ; group ; group = group->nextGroup ) {
for ( a = group->triList ; a ; a = a->next ) {
hashBounds.AddPoint( a->v[0].xyz );
hashBounds.AddPoint( a->v[1].xyz );
hashBounds.AddPoint( a->v[2].xyz );
}
}
// spread the bounds so it will never have a zero size
for ( i = 0 ; i < 3 ; i++ ) {
hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
if ( hashIntScale[i] < 1 ) {
hashIntScale[i] = 1;
}
}
// add all the points to the hash buckets
for ( group = groupList ; group ; group = group->nextGroup ) {
// don't create tjunctions against discrete surfaces (blood decals, etc)
if ( group->material != NULL && group->material->IsDiscrete() ) {
continue;
}
for ( a = group->triList ; a ; a = a->next ) {
for ( vert = 0 ; vert < 3 ; vert++ ) {
a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
}
}
}
}
/*
=================
FreeTJunctionHash
The optimizer may add some more crossing verts
after t junction processing
=================
*/
void FreeTJunctionHash( void ) {
int i, j, k;
hashVert_t *hv, *next;
for ( i = 0 ; i < HASH_BINS ; i++ ) {
for ( j = 0 ; j < HASH_BINS ; j++ ) {
for ( k = 0 ; k < HASH_BINS ; k++ ) {
for ( hv = hashVerts[i][j][k] ; hv ; hv = next ) {
next = hv->next;
Mem_Free( hv );
}
}
}
}
memset( hashVerts, 0, sizeof( hashVerts ) );
}
/*
==================
FixTriangleAgainstHashVert
Returns a list of two new mapTri if the hashVert is
on an edge of the given mapTri, otherwise returns NULL.
==================
*/
static mapTri_t *FixTriangleAgainstHashVert( const mapTri_t *a, const hashVert_t *hv ) {
int i;
const idDrawVert *v1, *v2, *v3;
idDrawVert split;
idVec3 dir;
float len;
float frac;
mapTri_t *new1, *new2;
idVec3 temp;
float d, off;
const idVec3 *v;
idPlane plane1, plane2;
v = &hv->v;
// if the triangle already has this hashVert as a vert,
// it can't be split by it
if ( a->hashVert[0] == hv || a->hashVert[1] == hv || a->hashVert[2] == hv ) {
return NULL;
}
// we probably should find the edge that the vertex is closest to.
// it is possible to be < 1 unit away from multiple
// edges, but we only want to split by one of them
for ( i = 0 ; i < 3 ; i++ ) {
v1 = &a->v[i];
v2 = &a->v[(i+1)%3];
v3 = &a->v[(i+2)%3];
VectorSubtract( v2->xyz, v1->xyz, dir );
len = dir.Normalize();
// if it is close to one of the edge vertexes, skip it
VectorSubtract( *v, v1->xyz, temp );
d = DotProduct( temp, dir );
if ( d <= 0 || d >= len ) {
continue;
}
// make sure it is on the line
VectorMA( v1->xyz, d, dir, temp );
VectorSubtract( temp, *v, temp );
off = temp.Length();
if ( off <= -COLINEAR_EPSILON || off >= COLINEAR_EPSILON ) {
continue;
}
// take the x/y/z from the splitter,
// but interpolate everything else from the original tri
VectorCopy( *v, split.xyz );
frac = d / len;
split.st[0] = v1->st[0] + frac * ( v2->st[0] - v1->st[0] );
split.st[1] = v1->st[1] + frac * ( v2->st[1] - v1->st[1] );
split.normal[0] = v1->normal[0] + frac * ( v2->normal[0] - v1->normal[0] );
split.normal[1] = v1->normal[1] + frac * ( v2->normal[1] - v1->normal[1] );
split.normal[2] = v1->normal[2] + frac * ( v2->normal[2] - v1->normal[2] );
split.normal.Normalize();
// split the tri
new1 = CopyMapTri( a );
new1->v[(i+1)%3] = split;
new1->hashVert[(i+1)%3] = hv;
new1->next = NULL;
new2 = CopyMapTri( a );
new2->v[i] = split;
new2->hashVert[i] = hv;
new2->next = new1;
plane1.FromPoints( new1->hashVert[0]->v, new1->hashVert[1]->v, new1->hashVert[2]->v );
plane2.FromPoints( new2->hashVert[0]->v, new2->hashVert[1]->v, new2->hashVert[2]->v );
d = DotProduct( plane1, plane2 );
// if the two split triangle's normals don't face the same way,
// it should not be split
if ( d <= 0 ) {
FreeTriList( new2 );
continue;
}
return new2;
}
return NULL;
}
/*
==================
FixTriangleAgainstHash
Potentially splits a triangle into a list of triangles based on tjunctions
==================
*/
static mapTri_t *FixTriangleAgainstHash( const mapTri_t *tri ) {
mapTri_t *fixed;
mapTri_t *a;
mapTri_t *test, *next;
int blocks[2][3];
int i, j, k;
hashVert_t *hv;
// if this triangle is degenerate after point snapping,
// do nothing (this shouldn't happen, because they should
// be removed as they are hashed)
if ( tri->hashVert[0] == tri->hashVert[1]
|| tri->hashVert[0] == tri->hashVert[2]
|| tri->hashVert[1] == tri->hashVert[2] ) {
return NULL;
}
fixed = CopyMapTri( tri );
fixed->next = NULL;
HashBlocksForTri( tri, blocks );
for ( i = blocks[0][0] ; i <= blocks[1][0] ; i++ ) {
for ( j = blocks[0][1] ; j <= blocks[1][1] ; j++ ) {
for ( k = blocks[0][2] ; k <= blocks[1][2] ; k++ ) {
for ( hv = hashVerts[i][j][k] ; hv ; hv = hv->next ) {
// fix all triangles in the list against this point
test = fixed;
fixed = NULL;
for ( ; test ; test = next ) {
next = test->next;
a = FixTriangleAgainstHashVert( test, hv );
if ( a ) {
// cut into two triangles
a->next->next = fixed;
fixed = a;
FreeTri( test );
} else {
test->next = fixed;
fixed = test;
}
}
}
}
}
}
return fixed;
}
/*
==================
CountGroupListTris
==================
*/
int CountGroupListTris( const optimizeGroup_t *groupList ) {
int c;
c = 0;
for ( ; groupList ; groupList = groupList->nextGroup ) {
c += CountTriList( groupList->triList );
}
return c;
}
/*
==================
FixAreaGroupsTjunctions
==================
*/
void FixAreaGroupsTjunctions( optimizeGroup_t *groupList ) {
const mapTri_t *tri;
mapTri_t *newList;
mapTri_t *fixed;
int startCount, endCount;
optimizeGroup_t *group;
if ( dmapGlobals.noTJunc ) {
return;
}
if ( !groupList ) {
return;
}
startCount = CountGroupListTris( groupList );
if ( dmapGlobals.verbose ) {
common->Printf( "----- FixAreaGroupsTjunctions -----\n" );
common->Printf( "%6i triangles in\n", startCount );
}
HashTriangles( groupList );
for ( group = groupList ; group ; group = group->nextGroup ) {
// don't touch discrete surfaces
if ( group->material != NULL && group->material->IsDiscrete() ) {
continue;
}
newList = NULL;
for ( tri = group->triList ; tri ; tri = tri->next ) {
fixed = FixTriangleAgainstHash( tri );
newList = MergeTriLists( newList, fixed );
}
FreeTriList( group->triList );
group->triList = newList;
}
endCount = CountGroupListTris( groupList );
if ( dmapGlobals.verbose ) {
common->Printf( "%6i triangles out\n", endCount );
}
}
/*
==================
FixEntityTjunctions
==================
*/
void FixEntityTjunctions( uEntity_t *e ) {
int i;
for ( i = 0 ; i < e->numAreas ; i++ ) {
FixAreaGroupsTjunctions( e->areas[i].groups );
FreeTJunctionHash();
}
}
/*
==================
FixGlobalTjunctions
==================
*/
void FixGlobalTjunctions( uEntity_t *e ) {
mapTri_t *a;
int vert;
int i;
optimizeGroup_t *group;
int areaNum;
common->Printf( "----- FixGlobalTjunctions -----\n" );
// clear the hash tables
memset( hashVerts, 0, sizeof( hashVerts ) );
numHashVerts = 0;
numTotalVerts = 0;
// bound all the triangles to determine the bucket size
hashBounds.Clear();
for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
for ( a = group->triList ; a ; a = a->next ) {
hashBounds.AddPoint( a->v[0].xyz );
hashBounds.AddPoint( a->v[1].xyz );
hashBounds.AddPoint( a->v[2].xyz );
}
}
}
// spread the bounds so it will never have a zero size
for ( i = 0 ; i < 3 ; i++ ) {
hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
if ( hashIntScale[i] < 1 ) {
hashIntScale[i] = 1;
}
}
// add all the points to the hash buckets
for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
// don't touch discrete surfaces
if ( group->material != NULL && group->material->IsDiscrete() ) {
continue;
}
for ( a = group->triList ; a ; a = a->next ) {
for ( vert = 0 ; vert < 3 ; vert++ ) {
a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
}
}
}
}
// add all the func_static model vertexes to the hash buckets
// optionally inline some of the func_static models
if ( dmapGlobals.entityNum == 0 ) {
for ( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ ) {
uEntity_t *entity = &dmapGlobals.uEntities[eNum];
const char *className = entity->mapEntity->epairs.GetString( "classname" );
if ( idStr::Icmp( className, "func_static" ) ) {
continue;
}
const char *modelName = entity->mapEntity->epairs.GetString( "model" );
if ( !modelName ) {
continue;
}
if ( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) ) {
continue;
}
idRenderModel *model = renderModelManager->FindModel( modelName );
// common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
idMat3 axis;
// get the rotation matrix in either full form, or single angle form
if ( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) ) {
float angle = entity->mapEntity->epairs.GetFloat( "angle" );
if ( angle != 0.0f ) {
axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
} else {
axis.Identity();
}
}
idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" );
for ( i = 0 ; i < model->NumSurfaces() ; i++ ) {
const modelSurface_t *surface = model->Surface( i );
const srfTriangles_t *tri = surface->geometry;
mapTri_t mapTri;
memset( &mapTri, 0, sizeof( mapTri ) );
mapTri.material = surface->shader;
// don't let discretes (autosprites, etc) merge together
if ( mapTri.material->IsDiscrete() ) {
mapTri.mergeGroup = (void *)surface;
}
for ( int j = 0 ; j < tri->numVerts ; j += 3 ) {
idVec3 v = tri->verts[j].xyz * axis + origin;
GetHashVert( v );
}
}
}
}
// now fix each area
for ( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
for ( group = e->areas[areaNum].groups ; group ; group = group->nextGroup ) {
// don't touch discrete surfaces
if ( group->material != NULL && group->material->IsDiscrete() ) {
continue;
}
mapTri_t *newList = NULL;
for ( mapTri_t *tri = group->triList ; tri ; tri = tri->next ) {
mapTri_t *fixed = FixTriangleAgainstHash( tri );
newList = MergeTriLists( newList, fixed );
}
FreeTriList( group->triList );
group->triList = newList;
}
}
// done
FreeTJunctionHash();
}

View File

@@ -0,0 +1,387 @@
/*
===========================================================================
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"
/*
All triangle list functions should behave reasonably with NULL lists.
*/
/*
===============
AllocTri
===============
*/
mapTri_t *AllocTri( void ) {
mapTri_t *tri;
tri = (mapTri_t *)Mem_Alloc( sizeof( *tri ) );
memset( tri, 0, sizeof( *tri ) );
return tri;
}
/*
===============
FreeTri
===============
*/
void FreeTri( mapTri_t *tri ) {
Mem_Free( tri );
}
/*
===============
MergeTriLists
This does not copy any tris, it just relinks them
===============
*/
mapTri_t *MergeTriLists( mapTri_t *a, mapTri_t *b ) {
mapTri_t **prev;
prev = &a;
while ( *prev ) {
prev = &(*prev)->next;
}
*prev = b;
return a;
}
/*
===============
FreeTriList
===============
*/
void FreeTriList( mapTri_t *a ) {
mapTri_t *next;
for ( ; a ; a = next ) {
next = a->next;
Mem_Free( a );
}
}
/*
===============
CopyTriList
===============
*/
mapTri_t *CopyTriList( const mapTri_t *a ) {
mapTri_t *testList;
const mapTri_t *tri;
testList = NULL;
for ( tri = a ; tri ; tri = tri->next ) {
mapTri_t *copy;
copy = CopyMapTri( tri );
copy ->next = testList;
testList = copy;
}
return testList;
}
/*
=============
CountTriList
=============
*/
int CountTriList( const mapTri_t *tri ) {
int c;
c = 0;
while ( tri ) {
c++;
tri = tri->next;
}
return c;
}
/*
===============
CopyMapTri
===============
*/
mapTri_t *CopyMapTri( const mapTri_t *tri ) {
mapTri_t *t;
t = (mapTri_t *)Mem_Alloc( sizeof( *t ) );
*t = *tri;
return t;
}
/*
===============
MapTriArea
===============
*/
float MapTriArea( const mapTri_t *tri ) {
return idWinding::TriangleArea( tri->v[0].xyz, tri->v[1].xyz, tri->v[2].xyz );
}
/*
===============
RemoveBadTris
Return a new list with any zero or negative area triangles removed
===============
*/
mapTri_t *RemoveBadTris( const mapTri_t *list ) {
mapTri_t *newList;
mapTri_t *copy;
const mapTri_t *tri;
newList = NULL;
for ( tri = list ; tri ; tri = tri->next ) {
if ( MapTriArea( tri ) > 0 ) {
copy = CopyMapTri( tri );
copy->next = newList;
newList = copy;
}
}
return newList;
}
/*
================
BoundTriList
================
*/
void BoundTriList( const mapTri_t *list, idBounds &b ) {
b.Clear();
for ( ; list ; list = list->next ) {
b.AddPoint( list->v[0].xyz );
b.AddPoint( list->v[1].xyz );
b.AddPoint( list->v[2].xyz );
}
}
/*
================
DrawTri
================
*/
void DrawTri( const mapTri_t *tri ) {
idWinding w;
w.SetNumPoints( 3 );
VectorCopy( tri->v[0].xyz, w[0] );
VectorCopy( tri->v[1].xyz, w[1] );
VectorCopy( tri->v[2].xyz, w[2] );
DrawWinding( &w );
}
/*
================
FlipTriList
Swaps the vertex order
================
*/
void FlipTriList( mapTri_t *tris ) {
mapTri_t *tri;
for ( tri = tris ; tri ; tri = tri->next ) {
idDrawVert v;
const struct hashVert_s *hv;
struct optVertex_s *ov;
v = tri->v[0];
tri->v[0] = tri->v[2];
tri->v[2] = v;
hv = tri->hashVert[0];
tri->hashVert[0] = tri->hashVert[2];
tri->hashVert[2] = hv;
ov = tri->optVert[0];
tri->optVert[0] = tri->optVert[2];
tri->optVert[2] = ov;
}
}
/*
================
WindingForTri
================
*/
idWinding *WindingForTri( const mapTri_t *tri ) {
idWinding *w;
w = new idWinding( 3 );
w->SetNumPoints( 3 );
VectorCopy( tri->v[0].xyz, (*w)[0] );
VectorCopy( tri->v[1].xyz, (*w)[1] );
VectorCopy( tri->v[2].xyz, (*w)[2] );
return w;
}
/*
================
TriVertsFromOriginal
Regenerate the texcoords and colors on a fragmented tri from the plane equations
================
*/
void TriVertsFromOriginal( mapTri_t *tri, const mapTri_t *original ) {
int i, j;
float denom;
denom = idWinding::TriangleArea( original->v[0].xyz, original->v[1].xyz, original->v[2].xyz );
if ( denom == 0 ) {
return; // original was degenerate, so it doesn't matter
}
for ( i = 0 ; i < 3 ; i++ ) {
float a, b, c;
// find the barycentric coordinates
a = idWinding::TriangleArea( tri->v[i].xyz, original->v[1].xyz, original->v[2].xyz ) / denom;
b = idWinding::TriangleArea( tri->v[i].xyz, original->v[2].xyz, original->v[0].xyz ) / denom;
c = idWinding::TriangleArea( tri->v[i].xyz, original->v[0].xyz, original->v[1].xyz ) / denom;
// regenerate the interpolated values
tri->v[i].st[0] = a * original->v[0].st[0]
+ b * original->v[1].st[0] + c * original->v[2].st[0];
tri->v[i].st[1] = a * original->v[0].st[1]
+ b * original->v[1].st[1] + c * original->v[2].st[1];
for ( j = 0 ; j < 3 ; j++ ) {
tri->v[i].normal[j] = a * original->v[0].normal[j]
+ b * original->v[1].normal[j] + c * original->v[2].normal[j];
}
tri->v[i].normal.Normalize();
}
}
/*
================
WindingToTriList
Generates a new list of triangles with proper texcoords from a winding
created by clipping the originalTri
OriginalTri can be NULL if you don't care about texCoords
================
*/
mapTri_t *WindingToTriList( const idWinding *w, const mapTri_t *originalTri ) {
mapTri_t *tri;
mapTri_t *triList;
int i, j;
const idVec3 *vec;
if ( !w ) {
return NULL;
}
triList = NULL;
for ( i = 2 ; i < w->GetNumPoints() ; i++ ) {
tri = AllocTri();
if ( !originalTri ) {
memset( tri, 0, sizeof( *tri ) );
} else {
*tri = *originalTri;
}
tri->next = triList;
triList = tri;
for ( j = 0 ; j < 3 ; j++ ) {
if ( j == 0 ) {
vec = &((*w)[0]).ToVec3();
} else if ( j == 1 ) {
vec = &((*w)[i-1]).ToVec3();
} else {
vec = &((*w)[i]).ToVec3();
}
VectorCopy( *vec, tri->v[j].xyz );
}
if ( originalTri ) {
TriVertsFromOriginal( tri, originalTri );
}
}
return triList;
}
/*
==================
ClipTriList
==================
*/
void ClipTriList( const mapTri_t *list, const idPlane &plane, float epsilon,
mapTri_t **front, mapTri_t **back ) {
const mapTri_t *tri;
mapTri_t *newList;
idWinding *w, *frontW, *backW;
*front = NULL;
*back = NULL;
for ( tri = list ; tri ; tri = tri->next ) {
w = WindingForTri( tri );
w->Split( plane, epsilon, &frontW, &backW );
newList = WindingToTriList( frontW, tri );
*front = MergeTriLists( *front, newList );
newList = WindingToTriList( backW, tri );
*back = MergeTriLists( *back, newList );
delete w;
}
}
/*
==================
PlaneForTri
==================
*/
void PlaneForTri( const mapTri_t *tri, idPlane &plane ) {
plane.FromPoints( tri->v[0].xyz, tri->v[1].xyz, tri->v[2].xyz );
}

View File

@@ -0,0 +1,709 @@
/*
===========================================================================
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"
int c_active_brushes;
int c_nodes;
// if a brush just barely pokes onto the other side,
// let it slide by without chopping
#define PLANESIDE_EPSILON 0.001
//0.1
/*
================
CountBrushList
================
*/
int CountBrushList (uBrush_t *brushes)
{
int c;
c = 0;
for ( ; brushes ; brushes = brushes->next)
c++;
return c;
}
int BrushSizeForSides( int numsides ) {
int c;
// allocate a structure with a variable number of sides at the end
// c = (int)&(((uBrush_t *)0)->sides[numsides]); // bounds checker complains about this
c = sizeof( uBrush_t ) + sizeof( side_t ) * (numsides - 6);
return c;
}
/*
================
AllocBrush
================
*/
uBrush_t *AllocBrush (int numsides)
{
uBrush_t *bb;
int c;
c = BrushSizeForSides( numsides );
bb = (uBrush_t *)Mem_Alloc(c);
memset (bb, 0, c);
c_active_brushes++;
return bb;
}
/*
================
FreeBrush
================
*/
void FreeBrush (uBrush_t *brushes)
{
int i;
for ( i = 0 ; i < brushes->numsides ; i++ ) {
if ( brushes->sides[i].winding ) {
delete brushes->sides[i].winding;
}
if ( brushes->sides[i].visibleHull ) {
delete brushes->sides[i].visibleHull;
}
}
Mem_Free (brushes);
c_active_brushes--;
}
/*
================
FreeBrushList
================
*/
void FreeBrushList (uBrush_t *brushes)
{
uBrush_t *next;
for ( ; brushes ; brushes = next)
{
next = brushes->next;
FreeBrush (brushes);
}
}
/*
==================
CopyBrush
Duplicates the brush, the sides, and the windings
==================
*/
uBrush_t *CopyBrush (uBrush_t *brush)
{
uBrush_t *newbrush;
int size;
int i;
size = BrushSizeForSides( brush->numsides );
newbrush = AllocBrush (brush->numsides);
memcpy (newbrush, brush, size);
for (i=0 ; i<brush->numsides ; i++)
{
if (brush->sides[i].winding)
newbrush->sides[i].winding = brush->sides[i].winding->Copy();
}
return newbrush;
}
/*
================
DrawBrushList
================
*/
void DrawBrushList (uBrush_t *brush)
{
int i;
side_t *s;
GLS_BeginScene ();
for ( ; brush ; brush=brush->next)
{
for (i=0 ; i<brush->numsides ; i++)
{
s = &brush->sides[i];
if (!s->winding)
continue;
GLS_Winding (s->winding, 0);
}
}
GLS_EndScene ();
}
/*
=============
PrintBrush
=============
*/
void PrintBrush (uBrush_t *brush)
{
int i;
common->Printf( "brush: %p\n", brush );
for ( i=0;i<brush->numsides ; i++ ) {
brush->sides[i].winding->Print();
common->Printf ("\n");
}
}
/*
==================
BoundBrush
Sets the mins/maxs based on the windings
returns false if the brush doesn't enclose a valid volume
==================
*/
bool BoundBrush (uBrush_t *brush) {
int i, j;
idWinding *w;
brush->bounds.Clear();
for ( i = 0; i < brush->numsides; i++ ) {
w = brush->sides[i].winding;
if (!w)
continue;
for ( j = 0; j < w->GetNumPoints(); j++ )
brush->bounds.AddPoint( (*w)[j].ToVec3() );
}
for ( i = 0; i < 3; i++ ) {
if (brush->bounds[0][i] < MIN_WORLD_COORD || brush->bounds[1][i] > MAX_WORLD_COORD
|| brush->bounds[0][i] >= brush->bounds[1][i] ) {
return false;
}
}
return true;
}
/*
==================
CreateBrushWindings
makes basewindigs for sides and mins / maxs for the brush
returns false if the brush doesn't enclose a valid volume
==================
*/
bool CreateBrushWindings (uBrush_t *brush) {
int i, j;
idWinding *w;
idPlane *plane;
side_t *side;
for ( i = 0; i < brush->numsides; i++ ) {
side = &brush->sides[i];
plane = &dmapGlobals.mapPlanes[side->planenum];
w = new idWinding( *plane );
for ( j = 0; j < brush->numsides && w; j++ ) {
if ( i == j ) {
continue;
}
if ( brush->sides[j].planenum == ( brush->sides[i].planenum ^ 1 ) ) {
continue; // back side clipaway
}
plane = &dmapGlobals.mapPlanes[brush->sides[j].planenum^1];
w = w->Clip( *plane, 0 );//CLIP_EPSILON);
}
if ( side->winding ) {
delete side->winding;
}
side->winding = w;
}
return BoundBrush( brush );
}
/*
==================
BrushFromBounds
Creates a new axial brush
==================
*/
uBrush_t *BrushFromBounds( const idBounds &bounds ) {
uBrush_t *b;
int i;
idPlane plane;
b = AllocBrush (6);
b->numsides = 6;
for (i=0 ; i<3 ; i++) {
plane[0] = plane[1] = plane[2] = 0;
plane[i] = 1;
plane[3] = -bounds[1][i];
b->sides[i].planenum = FindFloatPlane( plane );
plane[i] = -1;
plane[3] = bounds[0][i];
b->sides[3+i].planenum = FindFloatPlane( plane );
}
CreateBrushWindings (b);
return b;
}
/*
==================
BrushVolume
==================
*/
float BrushVolume (uBrush_t *brush) {
int i;
idWinding *w;
idVec3 corner;
float d, area, volume;
idPlane *plane;
if (!brush)
return 0;
// grab the first valid point as the corner
w = NULL;
for ( i = 0; i < brush->numsides; i++ ) {
w = brush->sides[i].winding;
if (w)
break;
}
if (!w) {
return 0;
}
VectorCopy ( (*w)[0], corner);
// make tetrahedrons to all other faces
volume = 0;
for ( ; i < brush->numsides; i++ )
{
w = brush->sides[i].winding;
if (!w)
continue;
plane = &dmapGlobals.mapPlanes[brush->sides[i].planenum];
d = -plane->Distance( corner );
area = w->GetArea();
volume += d * area;
}
volume /= 3;
return volume;
}
/*
==================
WriteBspBrushMap
FIXME: use new brush format
==================
*/
void WriteBspBrushMap( const char *name, uBrush_t *list ) {
idFile * f;
side_t * s;
int i;
idWinding * w;
common->Printf ("writing %s\n", name);
f = fileSystem->OpenFileWrite( name );
if ( !f ) {
common->Error( "Can't write %s\b", name);
}
f->Printf( "{\n\"classname\" \"worldspawn\"\n" );
for ( ; list ; list=list->next )
{
f->Printf( "{\n" );
for (i=0,s=list->sides ; i<list->numsides ; i++,s++)
{
w = new idWinding( dmapGlobals.mapPlanes[s->planenum] );
f->Printf ("( %i %i %i ) ", (int)(*w)[0][0], (int)(*w)[0][1], (int)(*w)[0][2]);
f->Printf ("( %i %i %i ) ", (int)(*w)[1][0], (int)(*w)[1][1], (int)(*w)[1][2]);
f->Printf ("( %i %i %i ) ", (int)(*w)[2][0], (int)(*w)[2][1], (int)(*w)[2][2]);
f->Printf ("notexture 0 0 0 1 1\n" );
delete w;
}
f->Printf ("}\n");
}
f->Printf ("}\n");
fileSystem->CloseFile(f);
}
//=====================================================================================
/*
====================
FilterBrushIntoTree_r
====================
*/
int FilterBrushIntoTree_r( uBrush_t *b, node_t *node ) {
uBrush_t *front, *back;
int c;
if ( !b ) {
return 0;
}
// add it to the leaf list
if ( node->planenum == PLANENUM_LEAF ) {
b->next = node->brushlist;
node->brushlist = b;
// classify the leaf by the structural brush
if ( b->opaque ) {
node->opaque = true;
}
return 1;
}
// split it by the node plane
SplitBrush ( b, node->planenum, &front, &back );
FreeBrush( b );
c = 0;
c += FilterBrushIntoTree_r( front, node->children[0] );
c += FilterBrushIntoTree_r( back, node->children[1] );
return c;
}
/*
=====================
FilterBrushesIntoTree
Mark the leafs as opaque and areaportals and put brush
fragments in each leaf so portal surfaces can be matched
to materials
=====================
*/
void FilterBrushesIntoTree( uEntity_t *e ) {
primitive_t *prim;
uBrush_t *b, *newb;
int r;
int c_unique, c_clusters;
common->Printf( "----- FilterBrushesIntoTree -----\n");
c_unique = 0;
c_clusters = 0;
for ( prim = e->primitives ; prim ; prim = prim->next ) {
b = prim->brush;
if ( !b ) {
continue;
}
c_unique++;
newb = CopyBrush( b );
r = FilterBrushIntoTree_r( newb, e->tree->headnode );
c_clusters += r;
}
common->Printf( "%5i total brushes\n", c_unique );
common->Printf( "%5i cluster references\n", c_clusters );
}
/*
================
AllocTree
================
*/
tree_t *AllocTree (void)
{
tree_t *tree;
tree = (tree_t *)Mem_Alloc(sizeof(*tree));
memset (tree, 0, sizeof(*tree));
tree->bounds.Clear();
return tree;
}
/*
================
AllocNode
================
*/
node_t *AllocNode (void)
{
node_t *node;
node = (node_t *)Mem_Alloc(sizeof(*node));
memset (node, 0, sizeof(*node));
return node;
}
//============================================================
/*
==================
BrushMostlyOnSide
==================
*/
int BrushMostlyOnSide (uBrush_t *brush, idPlane &plane) {
int i, j;
idWinding *w;
float d, max;
int side;
max = 0;
side = PSIDE_FRONT;
for ( i = 0; i < brush->numsides; i++ ) {
w = brush->sides[i].winding;
if (!w)
continue;
for ( j = 0; j < w->GetNumPoints(); j++ )
{
d = plane.Distance( (*w)[j].ToVec3() );
if (d > max)
{
max = d;
side = PSIDE_FRONT;
}
if (-d > max)
{
max = -d;
side = PSIDE_BACK;
}
}
}
return side;
}
/*
================
SplitBrush
Generates two new brushes, leaving the original
unchanged
================
*/
void SplitBrush (uBrush_t *brush, int planenum, uBrush_t **front, uBrush_t **back) {
uBrush_t *b[2];
int i, j;
idWinding *w, *cw[2], *midwinding;
side_t *s, *cs;
float d, d_front, d_back;
*front = *back = NULL;
idPlane &plane = dmapGlobals.mapPlanes[planenum];
// check all points
d_front = d_back = 0;
for ( i = 0; i < brush->numsides; i++ )
{
w = brush->sides[i].winding;
if (!w) {
continue;
}
for ( j = 0; j < w->GetNumPoints(); j++ ) {
d = plane.Distance( (*w)[j].ToVec3() );
if (d > 0 && d > d_front)
d_front = d;
if (d < 0 && d < d_back)
d_back = d;
}
}
if (d_front < 0.1) // PLANESIDE_EPSILON)
{ // only on back
*back = CopyBrush( brush );
return;
}
if (d_back > -0.1) // PLANESIDE_EPSILON)
{ // only on front
*front = CopyBrush( brush );
return;
}
// create a new winding from the split plane
w = new idWinding( plane );
for ( i = 0; i < brush->numsides && w; i++ ) {
idPlane &plane2 = dmapGlobals.mapPlanes[brush->sides[i].planenum ^ 1];
w = w->Clip( plane2, 0 ); // PLANESIDE_EPSILON);
}
if ( !w || w->IsTiny() ) {
// the brush isn't really split
int side;
side = BrushMostlyOnSide( brush, plane );
if (side == PSIDE_FRONT)
*front = CopyBrush (brush);
if (side == PSIDE_BACK)
*back = CopyBrush (brush);
return;
}
if ( w->IsHuge() ) {
common->Printf ("WARNING: huge winding\n");
}
midwinding = w;
// split it for real
for ( i = 0; i < 2; i++ ) {
b[i] = AllocBrush (brush->numsides+1);
memcpy( b[i], brush, sizeof( uBrush_t ) - sizeof( brush->sides ) );
b[i]->numsides = 0;
b[i]->next = NULL;
b[i]->original = brush->original;
}
// split all the current windings
for ( i = 0; i < brush->numsides; i++ ) {
s = &brush->sides[i];
w = s->winding;
if (!w)
continue;
w->Split( plane, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] );
for ( j = 0; j < 2; j++ ) {
if ( !cw[j] ) {
continue;
}
/*
if ( cw[j]->IsTiny() )
{
delete cw[j];
continue;
}
*/
cs = &b[j]->sides[b[j]->numsides];
b[j]->numsides++;
*cs = *s;
cs->winding = cw[j];
}
}
// see if we have valid polygons on both sides
for (i=0 ; i<2 ; i++)
{
if ( !BoundBrush (b[i]) ) {
break;
}
if ( b[i]->numsides < 3 )
{
FreeBrush (b[i]);
b[i] = NULL;
}
}
if ( !(b[0] && b[1]) )
{
if (!b[0] && !b[1])
common->Printf ("split removed brush\n");
else
common->Printf ("split not on both sides\n");
if (b[0])
{
FreeBrush (b[0]);
*front = CopyBrush (brush);
}
if (b[1])
{
FreeBrush (b[1]);
*back = CopyBrush (brush);
}
return;
}
// add the midwinding to both sides
for (i=0 ; i<2 ; i++)
{
cs = &b[i]->sides[b[i]->numsides];
b[i]->numsides++;
cs->planenum = planenum^i^1;
cs->material = NULL;
if (i==0)
cs->winding = midwinding->Copy();
else
cs->winding = midwinding;
}
{
float v1;
int i;
for (i=0 ; i<2 ; i++)
{
v1 = BrushVolume (b[i]);
if (v1 < 1.0)
{
FreeBrush (b[i]);
b[i] = NULL;
// common->Printf ("tiny volume after clip\n");
}
}
}
*front = b[0];
*back = b[1];
}

File diff suppressed because it is too large Load Diff