mirror of
https://github.com/id-Software/DOOM-3.git
synced 2026-03-20 08:59:56 +01:00
hello world
This commit is contained in:
404
neo/tools/compilers/dmap/dmap.cpp
Normal file
404
neo/tools/compilers/dmap/dmap.cpp
Normal 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();
|
||||
}
|
||||
485
neo/tools/compilers/dmap/dmap.h
Normal file
485
neo/tools/compilers/dmap/dmap.h
Normal 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 );
|
||||
499
neo/tools/compilers/dmap/facebsp.cpp
Normal file
499
neo/tools/compilers/dmap/facebsp.cpp
Normal 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;
|
||||
}
|
||||
|
||||
292
neo/tools/compilers/dmap/gldraw.cpp
Normal file
292
neo/tools/compilers/dmap/gldraw.cpp
Normal 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
|
||||
158
neo/tools/compilers/dmap/glfile.cpp
Normal file
158
neo/tools/compilers/dmap/glfile.cpp
Normal 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 );
|
||||
}
|
||||
|
||||
112
neo/tools/compilers/dmap/leakfile.cpp
Normal file
112
neo/tools/compilers/dmap/leakfile.cpp
Normal 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);
|
||||
}
|
||||
|
||||
648
neo/tools/compilers/dmap/map.cpp
Normal file
648
neo/tools/compilers/dmap/map.cpp
Normal 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 );
|
||||
}
|
||||
1990
neo/tools/compilers/dmap/optimize.cpp
Normal file
1990
neo/tools/compilers/dmap/optimize.cpp
Normal file
File diff suppressed because it is too large
Load Diff
84
neo/tools/compilers/dmap/optimize_gcc.cpp
Normal file
84
neo/tools/compilers/dmap/optimize_gcc.cpp
Normal 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;
|
||||
}
|
||||
682
neo/tools/compilers/dmap/output.cpp
Normal file
682
neo/tools/compilers/dmap/output.cpp
Normal 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 );
|
||||
}
|
||||
999
neo/tools/compilers/dmap/portals.cpp
Normal file
999
neo/tools/compilers/dmap/portals.cpp
Normal 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);
|
||||
}
|
||||
1276
neo/tools/compilers/dmap/shadowopt3.cpp
Normal file
1276
neo/tools/compilers/dmap/shadowopt3.cpp
Normal file
File diff suppressed because it is too large
Load Diff
663
neo/tools/compilers/dmap/tritjunction.cpp
Normal file
663
neo/tools/compilers/dmap/tritjunction.cpp
Normal 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();
|
||||
}
|
||||
387
neo/tools/compilers/dmap/tritools.cpp
Normal file
387
neo/tools/compilers/dmap/tritools.cpp
Normal 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 );
|
||||
}
|
||||
709
neo/tools/compilers/dmap/ubrush.cpp
Normal file
709
neo/tools/compilers/dmap/ubrush.cpp
Normal 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];
|
||||
}
|
||||
1046
neo/tools/compilers/dmap/usurface.cpp
Normal file
1046
neo/tools/compilers/dmap/usurface.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user