Merge pull request #150 from raynorpat/master

Add back dmap and aas compiler
This commit is contained in:
Robert Beckebans 2014-08-02 14:32:19 +02:00
commit f86bc286f6
45 changed files with 19973 additions and 169 deletions

View file

@ -1049,9 +1049,9 @@ set(RBDOOM3_INCLUDES
${DOOMCLASSIC_INCLUDES}
${TIMIDITY_INCLUDES}
#${COMPILER_INCLUDES}
#${COMPILER_AAS_INCLUDES} ${COMPILER_AAS_SOURCES}
#${COMPILER_DMAP_INCLUDES} ${COMPILER_DMAP_SOURCES}
${COMPILER_INCLUDES}
${COMPILER_AAS_INCLUDES} ${COMPILER_AAS_SOURCES}
${COMPILER_DMAP_INCLUDES} ${COMPILER_DMAP_SOURCES}
#${COMPILER_RENDERBUMP_INCLUDES} ${COMPILER_RENDERBUMP_SOURCES}
#${COMPILER_ROQVQ_INCLUDES} ${COMPILER_ROQVQ_SOURCES}
)
@ -1084,8 +1084,8 @@ set(RBDOOM3_SOURCES
${DOOMCLASSIC_SOURCES}
${TIMIDITY_SOURCES}
#${COMPILER_AAS_SOURCES}
#${COMPILER_DMAP_SOURCES}
${COMPILER_AAS_SOURCES}
${COMPILER_DMAP_SOURCES}
#${COMPILER_RENDERBUMP_SOURCES}
#${COMPILER_ROQVQ_SOURCES}
)

View file

@ -4453,6 +4453,14 @@ cmHandle_t idCollisionModelManagerLocal::LoadModel( const char* modelName )
ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( modelName );
if( models == NULL )
{
// raynorpat: best clear this if there are no models ( hit by dmap )
maxModels = MAX_SUBMODELS;
numModels = 0;
models = ( cm_model_t** ) Mem_ClearedAlloc( ( maxModels + 1 ) * sizeof( cm_model_t* ), TAG_COLLISION );
}
models[ numModels ] = LoadBinaryModel( generatedFileName, sourceTimeStamp );
if( models[ numModels ] != NULL )
{

View file

@ -1336,6 +1336,9 @@ void idCommonLocal::Init( int argc, const char* const* argv, const char* cmdline
// startup the script debugger
// DebuggerServerInit();
// Init tool commands
InitCommands();
// load the game dll
LoadGameDLL();
@ -1675,6 +1678,21 @@ void idCommonLocal::BusyWait()
session->Pump();
}
/*
===============
idCommonLocal::InitCommands
===============
*/
void idCommonLocal::InitCommands( void )
{
// compilers
cmdSystem->AddCommand( "dmap", Dmap_f, CMD_FL_TOOL, "compiles a map", idCmdSystem::ArgCompletion_MapName );
cmdSystem->AddCommand( "runAAS", RunAAS_f, CMD_FL_TOOL, "compiles an AAS file for a map", idCmdSystem::ArgCompletion_MapName );
cmdSystem->AddCommand( "runAASDir", RunAASDir_f, CMD_FL_TOOL, "compiles AAS files for all maps in a folder", idCmdSystem::ArgCompletion_MapName );
cmdSystem->AddCommand( "runReach", RunReach_f, CMD_FL_TOOL, "calculates reachability for an AAS file", idCmdSystem::ArgCompletion_MapName );
}
/*
===============
idCommonLocal::WaitForSessionState

View file

@ -115,18 +115,17 @@ ID_INLINE bool EndTraceRecording()
typedef enum
{
EDITOR_NONE = 0,
EDITOR_RADIANT = BIT( 1 ),
EDITOR_GUI = BIT( 2 ),
EDITOR_DEBUGGER = BIT( 3 ),
EDITOR_SCRIPT = BIT( 4 ),
EDITOR_LIGHT = BIT( 5 ),
EDITOR_SOUND = BIT( 6 ),
EDITOR_DECL = BIT( 7 ),
EDITOR_AF = BIT( 8 ),
EDITOR_PARTICLE = BIT( 9 ),
EDITOR_PDA = BIT( 10 ),
EDITOR_AAS = BIT( 11 ),
EDITOR_MATERIAL = BIT( 12 )
EDITOR_GUI = BIT( 1 ),
EDITOR_DEBUGGER = BIT( 2 ),
EDITOR_SCRIPT = BIT( 3 ),
EDITOR_LIGHT = BIT( 4 ),
EDITOR_SOUND = BIT( 5 ),
EDITOR_DECL = BIT( 6 ),
EDITOR_AF = BIT( 7 ),
EDITOR_PARTICLE = BIT( 8 ),
EDITOR_PDA = BIT( 9 ),
EDITOR_AAS = BIT( 10 ),
EDITOR_MATERIAL = BIT( 11 )
} toolFlag_t;
#define STRTABLE_ID "#str_"

View file

@ -94,7 +94,7 @@ ID_INLINE void idVectorSet<type, dimension>::Init( const type& mins, const type&
float boxSize;
idList<type>::AssureSize( initialSize );
idList<type>::SetNum( 0, false );
idList<type>::SetNum( 0 );
hash.Clear( idMath::IPow( boxHashSize, dimension ), initialSize );

View file

@ -166,6 +166,8 @@ public:
void SetTexCoordS( float s );
void SetTexCoordT( float t );
const idVec2 GetTexCoord() const;
const float GetTexCoordS() const;
const float GetTexCoordT() const;
const halfFloat_t GetTexCoordNativeS() const;
const halfFloat_t GetTexCoordNativeT() const;
@ -605,6 +607,26 @@ ID_INLINE const idVec2 idDrawVert::GetTexCoord() const
return idVec2( F16toF32( st[0] ), F16toF32( st[1] ) );
}
/*
========================
idDrawVert::GetTexCoordT
========================
*/
ID_INLINE const float idDrawVert::GetTexCoordS() const
{
return F16toF32( st[0] );
}
/*
========================
idDrawVert::GetTexCoordS
========================
*/
ID_INLINE const float idDrawVert::GetTexCoordT() const
{
return F16toF32( st[1] );
}
/*
========================
idDrawVert::GetTexCoordNativeS

View file

@ -1398,6 +1398,69 @@ bool idWinding::InsertPointIfOnEdge( const idVec5& point, const idPlane& plane,
return false;
}
/*
=============
idWinding::InsertPointIfOnEdge
=============
*/
bool idWinding::InsertPointIfOnEdge( const idVec3& point, const idPlane& plane, const float epsilon )
{
int i;
float dist, dot;
idVec3 normal;
// point may not be too far from the winding plane
if( idMath::Fabs( plane.Distance( point ) ) > epsilon )
{
return false;
}
for( i = 0; i < numPoints; i++ )
{
// create plane through edge orthogonal to winding plane
normal = ( p[( i + 1 ) % numPoints].ToVec3() - p[i].ToVec3() ).Cross( plane.Normal() );
normal.Normalize();
dist = normal * p[i].ToVec3();
if( idMath::Fabs( normal * point - dist ) > epsilon )
{
continue;
}
normal = plane.Normal().Cross( normal );
dot = normal * point;
dist = dot - normal * p[i].ToVec3();
if( dist < epsilon )
{
// if the winding already has the point
if( dist > -epsilon )
{
return false;
}
continue;
}
dist = dot - normal * p[( i + 1 ) % numPoints].ToVec3();
if( dist > -epsilon )
{
// if the winding already has the point
if( dist < epsilon )
{
return false;
}
continue;
}
InsertPoint( idVec5( point.x, point.y, point.z, 0, 0 ), i + 1 );
return true;
}
return false;
}
/*
=============
idWinding::IsTiny

View file

@ -87,6 +87,7 @@ public:
void RemovePoint( int point );
void InsertPoint( const idVec5& point, int spot );
bool InsertPointIfOnEdge( const idVec5& point, const idPlane& plane, const float epsilon = ON_EPSILON );
bool InsertPointIfOnEdge( const idVec3& point, const idPlane& plane, const float epsilon = ON_EPSILON );
// add a winding to the convex hull
void AddToConvexHull( const idWinding* winding, const idVec3& normal, const float epsilon = ON_EPSILON );
// add a point to the convex hull

View file

@ -132,6 +132,9 @@ const int MAX_EXPRESSION_REGISTERS = 4096;
#include "../sys/sys_session.h"
#include "../sys/sys_achievements.h"
// tools
#include "../tools/compilers/compiler_public.h"
//-----------------------------------------------------
#ifndef _D3SDK

View file

@ -143,4 +143,5 @@ MEM_TAG( PHYSICS_CLIP_ENTITY )
MEM_TAG( PHYSICS_BRITTLE )
MEM_TAG( PHYSICS_AF )
MEM_TAG( RENDERPROG )
MEM_TAG( TOOLS )
#undef MEM_TAG

View file

@ -29,7 +29,9 @@ If you have questions concerning this license or the applicable additional terms
#pragma hdrstop
#include "precompiled.h"
#include "../snd_local.h"
#if defined(USE_DOOMCLASSIC)
#include "../../../doomclassic/doom/i_sound.h"
#endif
idCVar s_showLevelMeter( "s_showLevelMeter", "0", CVAR_BOOL | CVAR_ARCHIVE, "Show VU meter" );
idCVar s_meterTopTime( "s_meterTopTime", "1000", CVAR_INTEGER | CVAR_ARCHIVE, "How long (in milliseconds) peaks are displayed on the VU meter" );
@ -336,10 +338,12 @@ void idSoundHardware_XAudio2::Init()
idSoundVoice::InitSurround( outputChannels, channelMask );
#if defined(USE_DOOMCLASSIC)
// ---------------------
// Initialize the Doom classic sound system.
// ---------------------
I_InitSoundHardware( outputChannels, channelMask );
#endif
// ---------------------
// Create VU Meter Effect
@ -424,10 +428,12 @@ void idSoundHardware_XAudio2::Shutdown()
freeVoices.Clear();
zombieVoices.Clear();
#if defined(USE_DOOMCLASSIC)
// ---------------------
// Shutdown the Doom classic sound system.
// ---------------------
I_ShutdownSoundHardware();
#endif
if( pXAudio2 != NULL )
{

View file

@ -258,35 +258,6 @@ void Sys_CPUCount( int& numLogicalCPUCores, int& numPhysicalCPUCores, int& numCP
}
// RB end
/*
================
Sys_GetSystemRam
returns in megabytes
================
*/
int Sys_GetSystemRam()
{
int mb;
long count, page_size;
count = sysconf( _SC_PHYS_PAGES );
if( count == -1 )
{
common->Printf( "GetSystemRam: sysconf _SC_PHYS_PAGES failed\n" );
return 512;
}
page_size = sysconf( _SC_PAGE_SIZE );
if( page_size == -1 )
{
common->Printf( "GetSystemRam: sysconf _SC_PAGE_SIZE failed\n" );
return 512;
}
mb = ( int )( ( double )count * ( double )page_size / ( 1024 * 1024 ) );
// round to the nearest 16Mb
mb = ( mb + 8 ) & ~15;
return mb;
}
/*
==================
Sys_DoStartProcess

View file

@ -166,32 +166,6 @@ void Sys_CPUCount( int& numLogicalCPUCores, int& numPhysicalCPUCores, int& numCP
}
// RB end
/*
================
Sys_GetSystemRam
returns in megabytes
================
*/
int Sys_GetSystemRam()
{
int mb, mib[2];
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
int64_t size = 0;
size_t len = sizeof( size );
if( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
{
mb = size / ( 1024 * 1024 );
mb = ( mb + 8 ) & ~15;
return mb;
}
common->Printf( "GetSystemRam: sysctl HW_MEMSIZE failed\n" );
return 512;
}
/*
==================
Sys_DoStartProcess

View file

@ -498,12 +498,6 @@ void Sys_FPU_SetFTZ( bool enable );
// sets Denormals-Are-Zero mode (only available when CPUID_DAZ is set)
void Sys_FPU_SetDAZ( bool enable );
// returns amount of system ram
int Sys_GetSystemRam();
// returns amount of video ram
int Sys_GetVideoRam();
// returns amount of drive space in path
int Sys_GetDriveFreeSpace( const char* path );

View file

@ -1140,8 +1140,14 @@ void Sys_Init() {
win32.sys_arch.SetString( "Win2K (NT)" );
} else if( win32.osversion.dwMajorVersion == 5 && win32.osversion.dwMinorVersion == 1 ) {
win32.sys_arch.SetString( "WinXP (NT)" );
} else if ( win32.osversion.dwMajorVersion == 6 ) {
} else if( win32.osversion.dwMajorVersion == 6 ) {
win32.sys_arch.SetString( "Vista" );
} else if( win32.osversion.dwMajorVersion == 6 && win32.osversion.dwMinorVersion == 1 ) {
win32.sys_arch.SetString( "Win7" );
} else if( win32.osversion.dwMajorVersion == 6 && win32.osversion.dwMinorVersion == 2 ) {
win32.sys_arch.SetString( "Win8" );
} else if( win32.osversion.dwMajorVersion == 6 && win32.osversion.dwMinorVersion == 3 ) {
win32.sys_arch.SetString( "Win8.1" );
} else {
win32.sys_arch.SetString( "Unknown NT variant" );
}
@ -1249,8 +1255,6 @@ void Sys_Init() {
}
common->Printf( "%s\n", win32.sys_cpustring.GetString() );
common->Printf( "%d MB System Memory\n", Sys_GetSystemRam() );
common->Printf( "%d MB Video Memory\n", Sys_GetVideoRam() );
if ( ( win32.cpuid & CPUID_SSE2 ) == 0 ) {
common->Error( "SSE2 not supported!" );
}

View file

@ -92,25 +92,6 @@ uint64 Sys_Microseconds()
return ( ( uint64 )( ( int64 )Sys_GetClockTicks() << 10 ) ) / ticksPerMicrosecondTimes1024;
}
/*
================
Sys_GetSystemRam
returns amount of physical memory in MB
================
*/
int Sys_GetSystemRam()
{
MEMORYSTATUSEX statex;
statex.dwLength = sizeof( statex );
GlobalMemoryStatusEx( &statex );
int physRam = statex.ullTotalPhys / ( 1024 * 1024 );
// HACK: For some reason, ullTotalPhys is sometimes off by a meg or two, so we round up to the nearest 16 megs
physRam = ( physRam + 8 ) & ~15;
return physRam;
}
/*
================
Sys_GetDriveFreeSpace
@ -150,74 +131,6 @@ int64 Sys_GetDriveFreeSpaceInBytes( const char* path )
return ret;
}
/*
================
Sys_GetVideoRam
returns in megabytes
================
*/
int Sys_GetVideoRam()
{
unsigned int retSize = 64;
// RB begin
#if !defined(__MINGW32__)
CComPtr<IWbemLocator> spLoc = NULL;
HRESULT hr = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_SERVER, IID_IWbemLocator, ( LPVOID* ) &spLoc );
if( hr != S_OK || spLoc == NULL )
{
return retSize;
}
CComBSTR bstrNamespace( _T( "\\\\.\\root\\CIMV2" ) );
CComPtr<IWbemServices> spServices;
// Connect to CIM
hr = spLoc->ConnectServer( bstrNamespace, NULL, NULL, 0, NULL, 0, 0, &spServices );
if( hr != WBEM_S_NO_ERROR )
{
return retSize;
}
// Switch the security level to IMPERSONATE so that provider will grant access to system-level objects.
hr = CoSetProxyBlanket( spServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
if( hr != S_OK )
{
return retSize;
}
// Get the vid controller
CComPtr<IEnumWbemClassObject> spEnumInst = NULL;
hr = spServices->CreateInstanceEnum( CComBSTR( "Win32_VideoController" ), WBEM_FLAG_SHALLOW, NULL, &spEnumInst );
if( hr != WBEM_S_NO_ERROR || spEnumInst == NULL )
{
return retSize;
}
ULONG uNumOfInstances = 0;
CComPtr<IWbemClassObject> spInstance = NULL;
hr = spEnumInst->Next( 10000, 1, &spInstance, &uNumOfInstances );
if( hr == S_OK && spInstance )
{
// Get properties from the object
CComVariant varSize;
hr = spInstance->Get( CComBSTR( _T( "AdapterRAM" ) ), 0, &varSize, 0, 0 );
if( hr == S_OK )
{
retSize = varSize.intVal / ( 1024 * 1024 );
if( retSize == 0 )
{
retSize = 64;
}
}
}
#endif
// RB end
return retSize;
}
/*
================
Sys_GetCurrentMemoryStatus

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,559 @@
/*
===========================================================================
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 "precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
#define VERTEX_HASH_BOXSIZE (1<<6) // must be power of 2
#define VERTEX_HASH_SIZE (VERTEX_HASH_BOXSIZE*VERTEX_HASH_BOXSIZE)
#define EDGE_HASH_SIZE (1<<14)
#define INTEGRAL_EPSILON 0.01f
#define VERTEX_EPSILON 0.1f
#define AAS_PLANE_NORMAL_EPSILON 0.00001f
#define AAS_PLANE_DIST_EPSILON 0.01f
idHashIndex* aas_vertexHash;
idHashIndex* aas_edgeHash;
idBounds aas_vertexBounds;
int aas_vertexShift;
/*
================
idAASBuild::SetupHash
================
*/
void idAASBuild::SetupHash( void )
{
aas_vertexHash = new idHashIndex( VERTEX_HASH_SIZE, 1024 );
aas_edgeHash = new idHashIndex( EDGE_HASH_SIZE, 1024 );
}
/*
================
idAASBuild::ShutdownHash
================
*/
void idAASBuild::ShutdownHash( void )
{
delete aas_vertexHash;
delete aas_edgeHash;
}
/*
================
idAASBuild::ClearHash
================
*/
void idAASBuild::ClearHash( const idBounds& bounds )
{
int i;
float f, max;
aas_vertexHash->Clear();
aas_edgeHash->Clear();
aas_vertexBounds = bounds;
max = bounds[1].x - bounds[0].x;
f = bounds[1].y - bounds[0].y;
if( f > max )
{
max = f;
}
aas_vertexShift = ( float ) max / VERTEX_HASH_BOXSIZE;
for( i = 0; ( 1 << i ) < aas_vertexShift; i++ )
{
}
if( i == 0 )
{
aas_vertexShift = 1;
}
else
{
aas_vertexShift = i;
}
}
/*
================
idAASBuild::HashVec
================
*/
ID_INLINE int idAASBuild::HashVec( const idVec3& vec )
{
int x, y;
x = ( ( ( int )( vec[0] - aas_vertexBounds[0].x + 0.5 ) ) + 2 ) >> 2;
y = ( ( ( int )( vec[1] - aas_vertexBounds[0].y + 0.5 ) ) + 2 ) >> 2;
return ( x + y * VERTEX_HASH_BOXSIZE ) & ( VERTEX_HASH_SIZE - 1 );
}
/*
================
idAASBuild::GetVertex
================
*/
bool idAASBuild::GetVertex( const idVec3& v, int* vertexNum )
{
int i, hashKey, vn;
aasVertex_t vert, *p;
for( i = 0; i < 3; i++ )
{
if( idMath::Fabs( v[i] - idMath::Rint( v[i] ) ) < INTEGRAL_EPSILON )
{
vert[i] = idMath::Rint( v[i] );
}
else
{
vert[i] = v[i];
}
}
hashKey = idAASBuild::HashVec( vert );
for( vn = aas_vertexHash->First( hashKey ); vn >= 0; vn = aas_vertexHash->Next( vn ) )
{
p = &file->vertices[vn];
// first compare z-axis because hash is based on x-y plane
if( idMath::Fabs( vert.z - p->z ) < VERTEX_EPSILON &&
idMath::Fabs( vert.x - p->x ) < VERTEX_EPSILON &&
idMath::Fabs( vert.y - p->y ) < VERTEX_EPSILON )
{
*vertexNum = vn;
return true;
}
}
*vertexNum = file->vertices.Num();
aas_vertexHash->Add( hashKey, file->vertices.Num() );
file->vertices.Append( vert );
return false;
}
/*
================
idAASBuild::GetEdge
================
*/
bool idAASBuild::GetEdge( const idVec3& v1, const idVec3& v2, int* edgeNum, int v1num )
{
int v2num, hashKey, e;
int* vertexNum;
aasEdge_t edge;
bool found;
if( v1num != -1 )
{
found = true;
}
else
{
found = GetVertex( v1, &v1num );
}
found &= GetVertex( v2, &v2num );
// if both vertexes are the same or snapped onto each other
if( v1num == v2num )
{
*edgeNum = 0;
return true;
}
hashKey = aas_edgeHash->GenerateKey( v1num, v2num );
// if both vertexes where already stored
if( found )
{
for( e = aas_edgeHash->First( hashKey ); e >= 0; e = aas_edgeHash->Next( e ) )
{
vertexNum = file->edges[e].vertexNum;
if( vertexNum[0] == v2num )
{
if( vertexNum[1] == v1num )
{
// negative for a reversed edge
*edgeNum = -e;
break;
}
}
else if( vertexNum[0] == v1num )
{
if( vertexNum[1] == v2num )
{
*edgeNum = e;
break;
}
}
}
// if edge found in hash
if( e >= 0 )
{
return true;
}
}
*edgeNum = file->edges.Num();
aas_edgeHash->Add( hashKey, file->edges.Num() );
edge.vertexNum[0] = v1num;
edge.vertexNum[1] = v2num;
file->edges.Append( edge );
return false;
}
/*
================
idAASBuild::GetFaceForPortal
================
*/
bool idAASBuild::GetFaceForPortal( idBrushBSPPortal* portal, int side, int* faceNum )
{
int i, j, v1num;
int numFaceEdges, faceEdges[MAX_POINTS_ON_WINDING];
idWinding* w;
aasFace_t face;
if( portal->GetFaceNum() > 0 )
{
if( side )
{
*faceNum = -portal->GetFaceNum();
}
else
{
*faceNum = portal->GetFaceNum();
}
return true;
}
w = portal->GetWinding();
// turn the winding into a sequence of edges
numFaceEdges = 0;
v1num = -1; // first vertex unknown
for( i = 0; i < w->GetNumPoints(); i++ )
{
GetEdge( ( *w )[i].ToVec3(), ( *w )[( i + 1 ) % w->GetNumPoints()].ToVec3(), &faceEdges[numFaceEdges], v1num );
if( faceEdges[numFaceEdges] )
{
// last vertex of this edge is the first vertex of the next edge
v1num = file->edges[abs( faceEdges[numFaceEdges] )].vertexNum[INT32_SIGNBITNOTSET( faceEdges[numFaceEdges] )];
// this edge is valid so keep it
numFaceEdges++;
}
}
// should have at least 3 edges
if( numFaceEdges < 3 )
{
return false;
}
// the polygon is invalid if some edge is found twice
for( i = 0; i < numFaceEdges; i++ )
{
for( j = i + 1; j < numFaceEdges; j++ )
{
if( faceEdges[i] == faceEdges[j] || faceEdges[i] == -faceEdges[j] )
{
return false;
}
}
}
portal->SetFaceNum( file->faces.Num() );
face.planeNum = file->planeList.FindPlane( portal->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
face.flags = portal->GetFlags();
face.areas[0] = face.areas[1] = 0;
face.firstEdge = file->edgeIndex.Num();
face.numEdges = numFaceEdges;
for( i = 0; i < numFaceEdges; i++ )
{
file->edgeIndex.Append( faceEdges[i] );
}
if( side )
{
*faceNum = -file->faces.Num();
}
else
{
*faceNum = file->faces.Num();
}
file->faces.Append( face );
return true;
}
/*
================
idAASBuild::GetAreaForLeafNode
================
*/
bool idAASBuild::GetAreaForLeafNode( idBrushBSPNode* node, int* areaNum )
{
int s, faceNum;
idBrushBSPPortal* p;
aasArea_t area;
if( node->GetAreaNum() )
{
*areaNum = -node->GetAreaNum();
return true;
}
area.flags = node->GetFlags();
area.cluster = area.clusterAreaNum = 0;
area.contents = node->GetContents();
area.firstFace = file->faceIndex.Num();
area.numFaces = 0;
area.reach = NULL;
area.rev_reach = NULL;
for( p = node->GetPortals(); p; p = p->Next( s ) )
{
s = ( p->GetNode( 1 ) == node );
if( !GetFaceForPortal( p, s, &faceNum ) )
{
continue;
}
file->faceIndex.Append( faceNum );
area.numFaces++;
if( faceNum > 0 )
{
file->faces[abs( faceNum )].areas[0] = file->areas.Num();
}
else
{
file->faces[abs( faceNum )].areas[1] = file->areas.Num();
}
}
if( !area.numFaces )
{
*areaNum = 0;
return false;
}
*areaNum = -file->areas.Num();
node->SetAreaNum( file->areas.Num() );
file->areas.Append( area );
DisplayRealTimeString( "\r%6d", file->areas.Num() );
return true;
}
/*
================
idAASBuild::StoreTree_r
================
*/
int idAASBuild::StoreTree_r( idBrushBSPNode* node )
{
int areaNum, nodeNum, child0, child1;
aasNode_t aasNode;
if( !node )
{
return 0;
}
if( node->GetContents() & AREACONTENTS_SOLID )
{
return 0;
}
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
{
if( GetAreaForLeafNode( node, &areaNum ) )
{
return areaNum;
}
return 0;
}
aasNode.planeNum = file->planeList.FindPlane( node->GetPlane(), AAS_PLANE_NORMAL_EPSILON, AAS_PLANE_DIST_EPSILON );
aasNode.children[0] = aasNode.children[1] = 0;
nodeNum = file->nodes.Num();
file->nodes.Append( aasNode );
// !@#$%^ cause of some bug we cannot set the children directly with the StoreTree_r return value
child0 = StoreTree_r( node->GetChild( 0 ) );
file->nodes[nodeNum].children[0] = child0;
child1 = StoreTree_r( node->GetChild( 1 ) );
file->nodes[nodeNum].children[1] = child1;
if( !child0 && !child1 )
{
file->nodes.SetNum( file->nodes.Num() - 1 );
return 0;
}
return nodeNum;
}
/*
================
idAASBuild::GetSizeEstimate_r
================
*/
typedef struct sizeEstimate_s
{
int numEdgeIndexes;
int numFaceIndexes;
int numAreas;
int numNodes;
} sizeEstimate_t;
void idAASBuild::GetSizeEstimate_r( idBrushBSPNode* parent, idBrushBSPNode* node, struct sizeEstimate_s& size )
{
idBrushBSPPortal* p;
int s;
if( !node )
{
return;
}
if( node->GetContents() & AREACONTENTS_SOLID )
{
return;
}
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
{
// multiple branches of the bsp tree might point to the same leaf node
if( node->GetParent() == parent )
{
size.numAreas++;
for( p = node->GetPortals(); p; p = p->Next( s ) )
{
s = ( p->GetNode( 1 ) == node );
size.numFaceIndexes++;
size.numEdgeIndexes += p->GetWinding()->GetNumPoints();
}
}
}
else
{
size.numNodes++;
}
GetSizeEstimate_r( node, node->GetChild( 0 ), size );
GetSizeEstimate_r( node, node->GetChild( 1 ), size );
}
/*
================
idAASBuild::SetSizeEstimate
================
*/
void idAASBuild::SetSizeEstimate( const idBrushBSP& bsp, idAASFileLocal* file )
{
sizeEstimate_t size;
size.numEdgeIndexes = 1;
size.numFaceIndexes = 1;
size.numAreas = 1;
size.numNodes = 1;
GetSizeEstimate_r( NULL, bsp.GetRootNode(), size );
file->planeList.Resize( size.numNodes / 2, 1024 );
file->vertices.Resize( size.numEdgeIndexes / 3, 1024 );
file->edges.Resize( size.numEdgeIndexes / 2, 1024 );
file->edgeIndex.Resize( size.numEdgeIndexes, 4096 );
file->faces.Resize( size.numFaceIndexes, 1024 );
file->faceIndex.Resize( size.numFaceIndexes, 4096 );
file->areas.Resize( size.numAreas, 1024 );
file->nodes.Resize( size.numNodes, 1024 );
}
/*
================
idAASBuild::StoreFile
================
*/
bool idAASBuild::StoreFile( const idBrushBSP& bsp )
{
aasEdge_t edge;
aasFace_t face;
aasArea_t area;
aasNode_t node;
common->Printf( "[Store AAS]\n" );
SetupHash();
ClearHash( bsp.GetTreeBounds() );
file = new idAASFileLocal();
file->Clear();
SetSizeEstimate( bsp, file );
// the first edge is a dummy
memset( &edge, 0, sizeof( edge ) );
file->edges.Append( edge );
// the first face is a dummy
memset( &face, 0, sizeof( face ) );
file->faces.Append( face );
// the first area is a dummy
memset( &area, 0, sizeof( area ) );
file->areas.Append( area );
// the first node is a dummy
memset( &node, 0, sizeof( node ) );
file->nodes.Append( node );
// store the tree
StoreTree_r( bsp.GetRootNode() );
// calculate area bounds and a reachable point in the area
file->FinishAreas();
ShutdownHash();
common->Printf( "\r%6d areas\n", file->areas.Num() );
return true;
}

View file

@ -0,0 +1,421 @@
/*
===========================================================================
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 "precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
/*
============
idAASBuild::SetPortalFlags_r
============
*/
void idAASBuild::SetPortalFlags_r( idBrushBSPNode* node )
{
int s;
idBrushBSPPortal* p;
idVec3 normal;
if( !node )
{
return;
}
if( node->GetContents() & AREACONTENTS_SOLID )
{
return;
}
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
{
for( p = node->GetPortals(); p; p = p->Next( s ) )
{
s = ( p->GetNode( 1 ) == node );
// if solid at the other side of the portal
if( p->GetNode( !s )->GetContents() & AREACONTENTS_SOLID )
{
if( s )
{
normal = -p->GetPlane().Normal();
}
else
{
normal = p->GetPlane().Normal();
}
if( normal * aasSettings->invGravityDir > aasSettings->minFloorCos )
{
p->SetFlag( FACE_FLOOR );
}
else
{
p->SetFlag( FACE_SOLID );
}
}
}
return;
}
SetPortalFlags_r( node->GetChild( 0 ) );
SetPortalFlags_r( node->GetChild( 1 ) );
}
/*
============
idAASBuild::PortalIsGap
============
*/
bool idAASBuild::PortalIsGap( idBrushBSPPortal* portal, int side )
{
idVec3 normal;
// if solid at the other side of the portal
if( portal->GetNode( !side )->GetContents() & AREACONTENTS_SOLID )
{
return false;
}
if( side )
{
normal = -( portal->GetPlane().Normal() );
}
else
{
normal = portal->GetPlane().Normal();
}
if( normal * aasSettings->invGravityDir > aasSettings->minFloorCos )
{
return true;
}
return false;
}
/*
============
idAASBuild::GravSubdivLeafNode
============
*/
#define FACE_CHECKED BIT(31)
#define GRAVSUBDIV_EPSILON 0.1f
void idAASBuild::GravSubdivLeafNode( idBrushBSPNode* node )
{
int s1, s2, i, j, k, side1;
int numSplits, numSplitters;
idBrushBSPPortal* p1, *p2;
idWinding* w1, *w2;
idVec3 normal;
idPlane plane;
idPlaneSet planeList;
float d, min, max;
int* splitterOrder;
int* bestNumSplits;
int floor, gap, numFloorChecked;
// if this leaf node is already classified it cannot have a combination of floor and gap portals
if( node->GetFlags() & ( AREA_FLOOR | AREA_GAP ) )
{
return;
}
floor = gap = 0;
// check if the area has a floor
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
{
s1 = ( p1->GetNode( 1 ) == node );
if( p1->GetFlags() & FACE_FLOOR )
{
floor++;
}
}
// find seperating planes between gap and floor portals
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
{
s1 = ( p1->GetNode( 1 ) == node );
// if the portal is a gap seen from this side
if( PortalIsGap( p1, s1 ) )
{
gap++;
// if the area doesn't have a floor
if( !floor )
{
break;
}
}
else
{
continue;
}
numFloorChecked = 0;
w1 = p1->GetWinding();
// test all edges of the gap
for( i = 0; i < w1->GetNumPoints(); i++ )
{
// create a plane through the edge of the gap parallel to the direction of gravity
normal = ( *w1 )[( i + 1 ) % w1->GetNumPoints()].ToVec3() - ( *w1 )[i].ToVec3();
normal = normal.Cross( aasSettings->invGravityDir );
if( normal.Normalize() < 0.2f )
{
continue;
}
plane.SetNormal( normal );
plane.FitThroughPoint( ( *w1 )[i].ToVec3() );
// get the side of the plane the gap is on
side1 = w1->PlaneSide( plane, GRAVSUBDIV_EPSILON );
if( side1 == SIDE_ON )
{
break;
}
// test if the plane through the edge of the gap seperates the gap from a floor portal
for( p2 = node->GetPortals(); p2; p2 = p2->Next( s2 ) )
{
s2 = ( p2->GetNode( 1 ) == node );
if( !( p2->GetFlags() & FACE_FLOOR ) )
{
continue;
}
if( p2->GetFlags() & FACE_CHECKED )
{
continue;
}
w2 = p2->GetWinding();
min = 2.0f * GRAVSUBDIV_EPSILON;
max = GRAVSUBDIV_EPSILON;
if( side1 == SIDE_FRONT )
{
for( j = 0; j < w2->GetNumPoints(); j++ )
{
d = plane.Distance( ( *w2 )[j].ToVec3() );
if( d >= GRAVSUBDIV_EPSILON )
{
break; // point at the same side of the plane as the gap
}
d = idMath::Fabs( d );
if( d < min )
{
min = d;
}
if( d > max )
{
max = d;
}
}
}
else
{
for( j = 0; j < w2->GetNumPoints(); j++ )
{
d = plane.Distance( ( *w2 )[j].ToVec3() );
if( d <= -GRAVSUBDIV_EPSILON )
{
break; // point at the same side of the plane as the gap
}
d = idMath::Fabs( d );
if( d < min )
{
min = d;
}
if( d > max )
{
max = d;
}
}
}
// a point of the floor portal was found to be at the same side of the plane as the gap
if( j < w2->GetNumPoints() )
{
continue;
}
// if the floor portal touches the plane
if( min < GRAVSUBDIV_EPSILON && max > GRAVSUBDIV_EPSILON )
{
planeList.FindPlane( plane, 0.00001f, 0.1f );
}
p2->SetFlag( FACE_CHECKED );
numFloorChecked++;
}
if( numFloorChecked == floor )
{
break;
}
}
for( p2 = node->GetPortals(); p2; p2 = p2->Next( s2 ) )
{
s2 = ( p2->GetNode( 1 ) == node );
p2->RemoveFlag( FACE_CHECKED );
}
}
// if the leaf node does not have both floor and gap portals
if( !( gap && floor ) )
{
if( floor )
{
node->SetFlag( AREA_FLOOR );
}
else if( gap )
{
node->SetFlag( AREA_GAP );
}
return;
}
// if no valid seperators found
if( planeList.Num() == 0 )
{
// NOTE: this should never happend, if it does the leaf node has degenerate portals
return;
}
splitterOrder = ( int* ) _alloca( planeList.Num() * sizeof( int ) );
bestNumSplits = ( int* ) _alloca( planeList.Num() * sizeof( int ) );
numSplitters = 0;
// test all possible seperators and sort them from best to worst
for( i = 0; i < planeList.Num(); i += 2 )
{
numSplits = 0;
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
{
s1 = ( p1->GetNode( 1 ) == node );
if( p1->GetWinding()->PlaneSide( planeList[i], 0.1f ) == SIDE_CROSS )
{
numSplits++;
}
}
for( j = 0; j < numSplitters; j++ )
{
if( numSplits < bestNumSplits[j] )
{
for( k = numSplitters; k > j; k-- )
{
bestNumSplits[k] = bestNumSplits[k - 1];
splitterOrder[k] = splitterOrder[k - 1];
}
bestNumSplits[j] = numSplits;
splitterOrder[j] = i;
numSplitters++;
break;
}
}
if( j >= numSplitters )
{
bestNumSplits[j] = numSplits;
splitterOrder[j] = i;
numSplitters++;
}
}
// try all seperators in order from best to worst
for( i = 0; i < numSplitters; i++ )
{
if( node->Split( planeList[splitterOrder[i]], -1 ) )
{
// we found a seperator that works
break;
}
}
if( i >= numSplitters )
{
return;
}
DisplayRealTimeString( "\r%6d", ++numGravitationalSubdivisions );
// test children for further splits
GravSubdivLeafNode( node->GetChild( 0 ) );
GravSubdivLeafNode( node->GetChild( 1 ) );
}
/*
============
idAASBuild::GravSubdiv_r
============
*/
void idAASBuild::GravSubdiv_r( idBrushBSPNode* node )
{
if( !node )
{
return;
}
if( node->GetContents() & AREACONTENTS_SOLID )
{
return;
}
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
{
GravSubdivLeafNode( node );
return;
}
GravSubdiv_r( node->GetChild( 0 ) );
GravSubdiv_r( node->GetChild( 1 ) );
}
/*
============
idAASBuild::GravitationalSubdivision
============
*/
void idAASBuild::GravitationalSubdivision( idBrushBSP& bsp )
{
numGravitationalSubdivisions = 0;
common->Printf( "[Gravitational Subdivision]\n" );
SetPortalFlags_r( bsp.GetRootNode() );
GravSubdiv_r( bsp.GetRootNode() );
common->Printf( "\r%6d subdivisions\n", numGravitationalSubdivisions );
}

View file

@ -0,0 +1,652 @@
/*
===========================================================================
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 "precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
#define LEDGE_EPSILON 0.1f
//===============================================================
//
// idLedge
//
//===============================================================
/*
============
idLedge::idLedge
============
*/
idLedge::idLedge( void )
{
}
/*
============
idLedge::idLedge
============
*/
idLedge::idLedge( const idVec3& v1, const idVec3& v2, const idVec3& gravityDir, idBrushBSPNode* n )
{
start = v1;
end = v2;
node = n;
numPlanes = 4;
planes[0].SetNormal( ( v1 - v2 ).Cross( gravityDir ) );
planes[0].Normalize();
planes[0].FitThroughPoint( v1 );
planes[1].SetNormal( ( v1 - v2 ).Cross( planes[0].Normal() ) );
planes[1].Normalize();
planes[1].FitThroughPoint( v1 );
planes[2].SetNormal( v1 - v2 );
planes[2].Normalize();
planes[2].FitThroughPoint( v1 );
planes[3].SetNormal( v2 - v1 );
planes[3].Normalize();
planes[3].FitThroughPoint( v2 );
}
/*
============
idLedge::AddPoint
============
*/
void idLedge::AddPoint( const idVec3& v )
{
if( planes[2].Distance( v ) > 0.0f )
{
start = v;
planes[2].FitThroughPoint( start );
}
if( planes[3].Distance( v ) > 0.0f )
{
end = v;
planes[3].FitThroughPoint( end );
}
}
/*
============
idLedge::CreateBevels
NOTE: this assumes the gravity is vertical
============
*/
void idLedge::CreateBevels( const idVec3& gravityDir )
{
int i, j;
idBounds bounds;
idVec3 size, normal;
bounds.Clear();
bounds.AddPoint( start );
bounds.AddPoint( end );
size = bounds[1] - bounds[0];
// plane through ledge
planes[0].SetNormal( ( start - end ).Cross( gravityDir ) );
planes[0].Normalize();
planes[0].FitThroughPoint( start );
// axial bevels at start and end point
i = size[1] > size[0];
normal = vec3_origin;
normal[i] = 1.0f;
j = end[i] > start[i];
planes[1 + j].SetNormal( normal );
planes[1 + !j].SetNormal( -normal );
planes[1].FitThroughPoint( start );
planes[2].FitThroughPoint( end );
numExpandedPlanes = 3;
// if additional bevels are required
if( idMath::Fabs( size[!i] ) > 0.01f )
{
normal = vec3_origin;
normal[!i] = 1.0f;
j = end[!i] > start[!i];
planes[3 + j].SetNormal( normal );
planes[3 + !j].SetNormal( -normal );
planes[3].FitThroughPoint( start );
planes[4].FitThroughPoint( end );
numExpandedPlanes = 5;
}
// opposite of first
planes[numExpandedPlanes + 0] = -planes[0];
// number of planes used for splitting
numSplitPlanes = numExpandedPlanes + 1;
// top plane
planes[numSplitPlanes + 0].SetNormal( ( start - end ).Cross( planes[0].Normal() ) );
planes[numSplitPlanes + 0].Normalize();
planes[numSplitPlanes + 0].FitThroughPoint( start );
// bottom plane
planes[numSplitPlanes + 1] = -planes[numSplitPlanes + 0];
// total number of planes
numPlanes = numSplitPlanes + 2;
}
/*
============
idLedge::Expand
============
*/
void idLedge::Expand( const idBounds& bounds, float maxStepHeight )
{
int i, j;
idVec3 v;
for( i = 0; i < numExpandedPlanes; i++ )
{
for( j = 0; j < 3; j++ )
{
if( planes[i].Normal()[j] > 0.0f )
{
v[j] = bounds[0][j];
}
else
{
v[j] = bounds[1][j];
}
}
planes[i].SetDist( planes[i].Dist() + v * -planes[i].Normal() );
}
planes[numSplitPlanes + 0].SetDist( planes[numSplitPlanes + 0].Dist() + maxStepHeight );
planes[numSplitPlanes + 1].SetDist( planes[numSplitPlanes + 1].Dist() + 1.0f );
}
/*
============
idLedge::ChopWinding
============
*/
idWinding* idLedge::ChopWinding( const idWinding* winding ) const
{
int i;
idWinding* w;
w = winding->Copy();
for( i = 0; i < numPlanes && w; i++ )
{
w = w->Clip( -planes[i], ON_EPSILON, true );
}
return w;
}
/*
============
idLedge::PointBetweenBounds
============
*/
bool idLedge::PointBetweenBounds( const idVec3& v ) const
{
return ( planes[2].Distance( v ) < LEDGE_EPSILON ) && ( planes[3].Distance( v ) < LEDGE_EPSILON );
}
//===============================================================
//
// idAASBuild
//
//===============================================================
/*
============
idAASBuild::LedgeSubdivFlood_r
============
*/
void idAASBuild::LedgeSubdivFlood_r( idBrushBSPNode* node, const idLedge* ledge )
{
int s1, i;
idBrushBSPPortal* p1;
idWinding* w;
idList<idBrushBSPNode*> nodeList;
if( node->GetFlags() & NODE_VISITED )
{
return;
}
// if this is not already a ledge area
if( !( node->GetFlags() & AREA_LEDGE ) )
{
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
{
s1 = ( p1->GetNode( 1 ) == node );
if( !( p1->GetFlags() & FACE_FLOOR ) )
{
continue;
}
// split the area if some part of the floor portal is inside the expanded ledge
w = ledge->ChopWinding( p1->GetWinding() );
if( !w )
{
continue;
}
delete w;
for( i = 0; i < ledge->numSplitPlanes; i++ )
{
if( node->PlaneSide( ledge->planes[i], 0.1f ) != SIDE_CROSS )
{
continue;
}
if( !node->Split( ledge->planes[i], -1 ) )
{
continue;
}
numLedgeSubdivisions++;
DisplayRealTimeString( "\r%6d", numLedgeSubdivisions );
node->GetChild( 0 )->SetFlag( NODE_VISITED );
LedgeSubdivFlood_r( node->GetChild( 1 ), ledge );
return;
}
node->SetFlag( AREA_LEDGE );
break;
}
}
node->SetFlag( NODE_VISITED );
// get all nodes we might need to flood into
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
{
s1 = ( p1->GetNode( 1 ) == node );
if( p1->GetNode( !s1 )->GetContents() & AREACONTENTS_SOLID )
{
continue;
}
// flood through this portal if the portal is partly inside the expanded ledge
w = ledge->ChopWinding( p1->GetWinding() );
if( !w )
{
continue;
}
delete w;
// add to list, cannot flood directly cause portals might be split on the way
nodeList.Append( p1->GetNode( !s1 ) );
}
// flood into other nodes
for( i = 0; i < nodeList.Num(); i++ )
{
LedgeSubdivLeafNodes_r( nodeList[i], ledge );
}
}
/*
============
idAASBuild::LedgeSubdivLeafNodes_r
The node the ledge was originally part of might be split by other ledges.
Here we recurse down the tree from the original node to find all the new leaf nodes the ledge might be part of.
============
*/
void idAASBuild::LedgeSubdivLeafNodes_r( idBrushBSPNode* node, const idLedge* ledge )
{
if( !node )
{
return;
}
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
{
LedgeSubdivFlood_r( node, ledge );
return;
}
LedgeSubdivLeafNodes_r( node->GetChild( 0 ), ledge );
LedgeSubdivLeafNodes_r( node->GetChild( 1 ), ledge );
}
/*
============
idAASBuild::LedgeSubdiv
============
*/
void idAASBuild::LedgeSubdiv( idBrushBSPNode* root )
{
int i, j;
idBrush* brush;
idList<idBrushSide*> sideList;
// create ledge bevels and expand ledges
for( i = 0; i < ledgeList.Num(); i++ )
{
ledgeList[i].CreateBevels( aasSettings->gravityDir );
ledgeList[i].Expand( aasSettings->boundingBoxes[0], aasSettings->maxStepHeight );
// if we should write out a ledge map
if( ledgeMap )
{
sideList.SetNum( 0 );
for( j = 0; j < ledgeList[i].numPlanes; j++ )
{
sideList.Append( new idBrushSide( ledgeList[i].planes[j], -1 ) );
}
brush = new idBrush();
brush->FromSides( sideList );
ledgeMap->WriteBrush( brush );
delete brush;
}
// flood tree from the ledge node and subdivide areas with the ledge
LedgeSubdivLeafNodes_r( ledgeList[i].node, &ledgeList[i] );
// remove the node visited flags
ledgeList[i].node->RemoveFlagRecurseFlood( NODE_VISITED );
}
}
/*
============
idAASBuild::IsLedgeSide_r
============
*/
bool idAASBuild::IsLedgeSide_r( idBrushBSPNode* node, idFixedWinding* w, const idPlane& plane, const idVec3& normal, const idVec3& origin, const float radius )
{
int res, i;
idFixedWinding back;
float dist;
if( !node )
{
return false;
}
while( node->GetChild( 0 ) && node->GetChild( 1 ) )
{
dist = node->GetPlane().Distance( origin );
if( dist > radius )
{
res = SIDE_FRONT;
}
else if( dist < -radius )
{
res = SIDE_BACK;
}
else
{
res = w->Split( &back, node->GetPlane(), LEDGE_EPSILON );
}
if( res == SIDE_FRONT )
{
node = node->GetChild( 0 );
}
else if( res == SIDE_BACK )
{
node = node->GetChild( 1 );
}
else if( res == SIDE_ON )
{
// continue with the side the winding faces
if( node->GetPlane().Normal() * normal > 0.0f )
{
node = node->GetChild( 0 );
}
else
{
node = node->GetChild( 1 );
}
}
else
{
if( IsLedgeSide_r( node->GetChild( 1 ), &back, plane, normal, origin, radius ) )
{
return true;
}
node = node->GetChild( 0 );
}
}
if( node->GetContents() & AREACONTENTS_SOLID )
{
return false;
}
for( i = 0; i < w->GetNumPoints(); i++ )
{
if( plane.Distance( ( *w )[i].ToVec3() ) > 0.0f )
{
return true;
}
}
return false;
}
/*
============
idAASBuild::AddLedge
============
*/
void idAASBuild::AddLedge( const idVec3& v1, const idVec3& v2, idBrushBSPNode* node )
{
int i, j, merged;
// first try to merge the ledge with existing ledges
merged = -1;
for( i = 0; i < ledgeList.Num(); i++ )
{
for( j = 0; j < 2; j++ )
{
if( idMath::Fabs( ledgeList[i].planes[j].Distance( v1 ) ) > LEDGE_EPSILON )
{
break;
}
if( idMath::Fabs( ledgeList[i].planes[j].Distance( v2 ) ) > LEDGE_EPSILON )
{
break;
}
}
if( j < 2 )
{
continue;
}
if( !ledgeList[i].PointBetweenBounds( v1 ) &&
!ledgeList[i].PointBetweenBounds( v2 ) )
{
continue;
}
if( merged == -1 )
{
ledgeList[i].AddPoint( v1 );
ledgeList[i].AddPoint( v2 );
merged = i;
}
else
{
ledgeList[merged].AddPoint( ledgeList[i].start );
ledgeList[merged].AddPoint( ledgeList[i].end );
ledgeList.RemoveIndex( i );
break;
}
}
// if the ledge could not be merged
if( merged == -1 )
{
ledgeList.Append( idLedge( v1, v2, aasSettings->gravityDir, node ) );
}
}
/*
============
idAASBuild::FindLeafNodeLedges
============
*/
void idAASBuild::FindLeafNodeLedges( idBrushBSPNode* root, idBrushBSPNode* node )
{
int s1, i;
idBrushBSPPortal* p1;
idWinding* w;
idVec3 v1, v2, normal, origin;
idFixedWinding winding;
idBounds bounds;
idPlane plane;
float radius;
for( p1 = node->GetPortals(); p1; p1 = p1->Next( s1 ) )
{
s1 = ( p1->GetNode( 1 ) == node );
if( !( p1->GetFlags() & FACE_FLOOR ) )
{
continue;
}
if( s1 )
{
plane = p1->GetPlane();
w = p1->GetWinding()->Reverse();
}
else
{
plane = -p1->GetPlane();
w = p1->GetWinding();
}
for( i = 0; i < w->GetNumPoints(); i++ )
{
v1 = ( *w )[i].ToVec3();
v2 = ( *w )[( i + 1 ) % w->GetNumPoints()].ToVec3();
normal = ( v2 - v1 ).Cross( aasSettings->gravityDir );
if( normal.Normalize() < 0.5f )
{
continue;
}
winding.Clear();
winding += v1 + normal * LEDGE_EPSILON * 0.5f;
winding += v2 + normal * LEDGE_EPSILON * 0.5f;
winding += winding[1].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir;
winding += winding[0].ToVec3() + ( aasSettings->maxStepHeight + 1.0f ) * aasSettings->gravityDir;
winding.GetBounds( bounds );
origin = ( bounds[1] - bounds[0] ) * 0.5f;
radius = origin.Length() + LEDGE_EPSILON;
origin = bounds[0] + origin;
plane.FitThroughPoint( v1 + aasSettings->maxStepHeight * aasSettings->gravityDir );
if( !IsLedgeSide_r( root, &winding, plane, normal, origin, radius ) )
{
continue;
}
AddLedge( v1, v2, node );
}
if( w != p1->GetWinding() )
{
delete w;
}
}
}
/*
============
idAASBuild::FindLedges_r
============
*/
void idAASBuild::FindLedges_r( idBrushBSPNode* root, idBrushBSPNode* node )
{
if( !node )
{
return;
}
if( node->GetContents() & AREACONTENTS_SOLID )
{
return;
}
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
{
if( node->GetFlags() & NODE_VISITED )
{
return;
}
FindLeafNodeLedges( root, node );
node->SetFlag( NODE_VISITED );
return;
}
FindLedges_r( root, node->GetChild( 0 ) );
FindLedges_r( root, node->GetChild( 1 ) );
}
/*
============
idAASBuild::WriteLedgeMap
============
*/
void idAASBuild::WriteLedgeMap( const idStr& fileName, const idStr& ext )
{
ledgeMap = new idBrushMap( fileName, ext );
ledgeMap->SetTexture( "textures/base_trim/bluetex4q_ed" );
}
/*
============
idAASBuild::LedgeSubdivision
NOTE: this assumes the bounding box is higher than the maximum step height
only ledges with vertical sides are considered
============
*/
void idAASBuild::LedgeSubdivision( idBrushBSP& bsp )
{
numLedgeSubdivisions = 0;
ledgeList.Clear();
common->Printf( "[Ledge Subdivision]\n" );
bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED );
FindLedges_r( bsp.GetRootNode(), bsp.GetRootNode() );
bsp.GetRootNode()->RemoveFlagRecurse( NODE_VISITED );
common->Printf( "\r%6d ledges\n", ledgeList.Num() );
LedgeSubdiv( bsp.GetRootNode() );
common->Printf( "\r%6d subdivisions\n", numLedgeSubdivisions );
}

View file

@ -0,0 +1,152 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASBUILD_LOCAL_H__
#define __AASBUILD_LOCAL_H__
#include "..\..\aas\AASFile.h"
#include "..\..\aas\AASFile_local.h"
#include "Brush.h"
#include "BrushBSP.h"
#include "AASReach.h"
#include "AASCluster.h"
//===============================================================
//
// idAASBuild
//
//===============================================================
typedef struct aasProcNode_s
{
idPlane plane;
int children[2]; // negative numbers are (-1 - areaNumber), 0 = solid
} aasProcNode_t;
class idLedge
{
public:
idVec3 start;
idVec3 end;
idBrushBSPNode* node;
int numExpandedPlanes;
int numSplitPlanes;
int numPlanes;
idPlane planes[8];
public:
idLedge( void );
idLedge( const idVec3& v1, const idVec3& v2, const idVec3& gravityDir, idBrushBSPNode* n );
void AddPoint( const idVec3& v );
void CreateBevels( const idVec3& gravityDir );
void Expand( const idBounds& bounds, float maxStepHeight );
idWinding* ChopWinding( const idWinding* winding ) const;
bool PointBetweenBounds( const idVec3& v ) const;
};
class idAASBuild
{
public:
idAASBuild( void );
~idAASBuild( void );
bool Build( const idStr& fileName, const idAASSettings* settings );
bool BuildReachability( const idStr& fileName, const idAASSettings* settings );
void Shutdown( void );
private:
const idAASSettings* aasSettings;
idAASFileLocal* file;
aasProcNode_t* procNodes;
int numProcNodes;
int numGravitationalSubdivisions;
int numMergedLeafNodes;
int numLedgeSubdivisions;
idList<idLedge> ledgeList;
idBrushMap* ledgeMap;
private: // map loading
void ParseProcNodes( idLexer* src );
bool LoadProcBSP( const char* name, ID_TIME_T minFileTime );
void DeleteProcBSP( void );
bool ChoppedAwayByProcBSP( int nodeNum, idFixedWinding* w, const idVec3& normal, const idVec3& origin, const float radius );
void ClipBrushSidesWithProcBSP( idBrushList& brushList );
int ContentsForAAS( int contents );
idBrushList AddBrushesForMapBrush( const idMapBrush* mapBrush, const idVec3& origin, const idMat3& axis, int entityNum, int primitiveNum, idBrushList brushList );
idBrushList AddBrushesForMapPatch( const idMapPatch* mapPatch, const idVec3& origin, const idMat3& axis, int entityNum, int primitiveNum, idBrushList brushList );
idBrushList AddBrushesForMapEntity( const idMapEntity* mapEnt, int entityNum, idBrushList brushList );
idBrushList AddBrushesForMapFile( const idMapFile* mapFile, idBrushList brushList );
bool CheckForEntities( const idMapFile* mapFile, idStrList& entityClassNames ) const;
void ChangeMultipleBoundingBoxContents_r( idBrushBSPNode* node, int mask );
private: // gravitational subdivision
void SetPortalFlags_r( idBrushBSPNode* node );
bool PortalIsGap( idBrushBSPPortal* portal, int side );
void GravSubdivLeafNode( idBrushBSPNode* node );
void GravSubdiv_r( idBrushBSPNode* node );
void GravitationalSubdivision( idBrushBSP& bsp );
private: // ledge subdivision
void LedgeSubdivFlood_r( idBrushBSPNode* node, const idLedge* ledge );
void LedgeSubdivLeafNodes_r( idBrushBSPNode* node, const idLedge* ledge );
void LedgeSubdiv( idBrushBSPNode* root );
bool IsLedgeSide_r( idBrushBSPNode* node, idFixedWinding* w, const idPlane& plane, const idVec3& normal, const idVec3& origin, const float radius );
void AddLedge( const idVec3& v1, const idVec3& v2, idBrushBSPNode* node );
void FindLeafNodeLedges( idBrushBSPNode* root, idBrushBSPNode* node );
void FindLedges_r( idBrushBSPNode* root, idBrushBSPNode* node );
void LedgeSubdivision( idBrushBSP& bsp );
void WriteLedgeMap( const idStr& fileName, const idStr& ext );
private: // merging
bool AllGapsLeadToOtherNode( idBrushBSPNode* nodeWithGaps, idBrushBSPNode* otherNode );
bool MergeWithAdjacentLeafNodes( idBrushBSP& bsp, idBrushBSPNode* node );
void MergeLeafNodes_r( idBrushBSP& bsp, idBrushBSPNode* node );
void MergeLeafNodes( idBrushBSP& bsp );
private: // storing file
void SetupHash( void );
void ShutdownHash( void );
void ClearHash( const idBounds& bounds );
int HashVec( const idVec3& vec );
bool GetVertex( const idVec3& v, int* vertexNum );
bool GetEdge( const idVec3& v1, const idVec3& v2, int* edgeNum, int v1num );
bool GetFaceForPortal( idBrushBSPPortal* portal, int side, int* faceNum );
bool GetAreaForLeafNode( idBrushBSPNode* node, int* areaNum );
int StoreTree_r( idBrushBSPNode* node );
void GetSizeEstimate_r( idBrushBSPNode* parent, idBrushBSPNode* node, struct sizeEstimate_s& size );
void SetSizeEstimate( const idBrushBSP& bsp, idAASFileLocal* file );
bool StoreFile( const idBrushBSP& bsp );
};
#endif /* !__AASBUILD_LOCAL_H__ */

View file

@ -0,0 +1,188 @@
/*
===========================================================================
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 "precompiled.h"
#pragma hdrstop
#include "AASBuild_local.h"
/*
============
idAASBuild::AllGapsLeadToOtherNode
============
*/
bool idAASBuild::AllGapsLeadToOtherNode( idBrushBSPNode* nodeWithGaps, idBrushBSPNode* otherNode )
{
int s;
idBrushBSPPortal* p;
for( p = nodeWithGaps->GetPortals(); p; p = p->Next( s ) )
{
s = ( p->GetNode( 1 ) == nodeWithGaps );
if( !PortalIsGap( p, s ) )
{
continue;
}
if( p->GetNode( !s ) != otherNode )
{
return false;
}
}
return true;
}
/*
============
idAASBuild::MergeWithAdjacentLeafNodes
============
*/
bool idAASBuild::MergeWithAdjacentLeafNodes( idBrushBSP& bsp, idBrushBSPNode* node )
{
int s, numMerges = 0, otherNodeFlags;
idBrushBSPPortal* p;
do
{
for( p = node->GetPortals(); p; p = p->Next( s ) )
{
s = ( p->GetNode( 1 ) == node );
// both leaf nodes must have the same contents
if( node->GetContents() != p->GetNode( !s )->GetContents() )
{
continue;
}
// cannot merge leaf nodes if one is near a ledge and the other is not
if( ( node->GetFlags() & AREA_LEDGE ) != ( p->GetNode( !s )->GetFlags() & AREA_LEDGE ) )
{
continue;
}
// cannot merge leaf nodes if one has a floor portal and the other a gap portal
if( node->GetFlags() & AREA_FLOOR )
{
if( p->GetNode( !s )->GetFlags() & AREA_GAP )
{
if( !AllGapsLeadToOtherNode( p->GetNode( !s ), node ) )
{
continue;
}
}
}
else if( node->GetFlags() & AREA_GAP )
{
if( p->GetNode( !s )->GetFlags() & AREA_FLOOR )
{
if( !AllGapsLeadToOtherNode( node, p->GetNode( !s ) ) )
{
continue;
}
}
}
otherNodeFlags = p->GetNode( !s )->GetFlags();
// try to merge the leaf nodes
if( bsp.TryMergeLeafNodes( p, s ) )
{
node->SetFlag( otherNodeFlags );
if( node->GetFlags() & AREA_FLOOR )
{
node->RemoveFlag( AREA_GAP );
}
numMerges++;
DisplayRealTimeString( "\r%6d", ++numMergedLeafNodes );
break;
}
}
}
while( p );
if( numMerges )
{
return true;
}
return false;
}
/*
============
idAASBuild::MergeLeafNodes_r
============
*/
void idAASBuild::MergeLeafNodes_r( idBrushBSP& bsp, idBrushBSPNode* node )
{
if( !node )
{
return;
}
if( node->GetContents() & AREACONTENTS_SOLID )
{
return;
}
if( node->GetFlags() & NODE_DONE )
{
return;
}
if( !node->GetChild( 0 ) && !node->GetChild( 1 ) )
{
MergeWithAdjacentLeafNodes( bsp, node );
node->SetFlag( NODE_DONE );
return;
}
MergeLeafNodes_r( bsp, node->GetChild( 0 ) );
MergeLeafNodes_r( bsp, node->GetChild( 1 ) );
return;
}
/*
============
idAASBuild::MergeLeafNodes
============
*/
void idAASBuild::MergeLeafNodes( idBrushBSP& bsp )
{
numMergedLeafNodes = 0;
common->Printf( "[Merge Leaf Nodes]\n" );
MergeLeafNodes_r( bsp, bsp.GetRootNode() );
bsp.GetRootNode()->RemoveFlagRecurse( NODE_DONE );
bsp.PruneMergedTree_r( bsp.GetRootNode() );
common->Printf( "\r%6d leaf nodes merged\n", numMergedLeafNodes );
}

View file

@ -0,0 +1,645 @@
/*
===========================================================================
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 "precompiled.h"
#pragma hdrstop
#include "..\..\aas\AASFile.h"
#include "..\..\aas\AASFile_local.h"
#include "AASCluster.h"
/*
================
idAASCluster::UpdatePortal
================
*/
bool idAASCluster::UpdatePortal( int areaNum, int clusterNum )
{
int portalNum;
aasPortal_t* portal;
// find the portal for this area
for( portalNum = 1; portalNum < file->portals.Num(); portalNum++ )
{
if( file->portals[portalNum].areaNum == areaNum )
{
break;
}
}
if( portalNum >= file->portals.Num() )
{
common->Error( "no portal for area %d", areaNum );
return true;
}
portal = &file->portals[portalNum];
// if the portal is already fully updated
if( portal->clusters[0] == clusterNum )
{
return true;
}
if( portal->clusters[1] == clusterNum )
{
return true;
}
// if the portal has no front cluster yet
if( !portal->clusters[0] )
{
portal->clusters[0] = clusterNum;
}
// if the portal has no back cluster yet
else if( !portal->clusters[1] )
{
portal->clusters[1] = clusterNum;
}
else
{
// remove the cluster portal flag contents
file->areas[areaNum].contents &= ~AREACONTENTS_CLUSTERPORTAL;
return false;
}
// set the area cluster number to the negative portal number
file->areas[areaNum].cluster = -portalNum;
// add the portal to the cluster using the portal index
file->portalIndex.Append( portalNum );
file->clusters[clusterNum].numPortals++;
return true;
}
/*
================
idAASCluster::FloodClusterAreas_r
================
*/
bool idAASCluster::FloodClusterAreas_r( int areaNum, int clusterNum )
{
aasArea_t* area;
aasFace_t* face;
int faceNum, i;
idReachability* reach;
area = &file->areas[areaNum];
// if the area is already part of a cluster
if( area->cluster > 0 )
{
if( area->cluster == clusterNum )
{
return true;
}
// there's a reachability going from one cluster to another only in one direction
common->Error( "cluster %d touched cluster %d at area %d\r\n", clusterNum, file->areas[areaNum].cluster, areaNum );
return false;
}
// if this area is a cluster portal
if( area->contents & AREACONTENTS_CLUSTERPORTAL )
{
return UpdatePortal( areaNum, clusterNum );
}
// set the area cluster number
area->cluster = clusterNum;
if( !noFaceFlood )
{
// use area faces to flood into adjacent areas
for( i = 0; i < area->numFaces; i++ )
{
faceNum = abs( file->faceIndex[area->firstFace + i] );
face = &file->faces[faceNum];
if( face->areas[0] == areaNum )
{
if( face->areas[1] )
{
if( !FloodClusterAreas_r( face->areas[1], clusterNum ) )
{
return false;
}
}
}
else
{
if( face->areas[0] )
{
if( !FloodClusterAreas_r( face->areas[0], clusterNum ) )
{
return false;
}
}
}
}
}
// use the reachabilities to flood into other areas
for( reach = file->areas[areaNum].reach; reach; reach = reach->next )
{
if( !FloodClusterAreas_r( reach->toAreaNum, clusterNum ) )
{
return false;
}
}
// use the reversed reachabilities to flood into other areas
for( reach = file->areas[areaNum].rev_reach; reach; reach = reach->rev_next )
{
if( !FloodClusterAreas_r( reach->fromAreaNum, clusterNum ) )
{
return false;
}
}
return true;
}
/*
================
idAASCluster::RemoveAreaClusterNumbers
================
*/
void idAASCluster::RemoveAreaClusterNumbers( void )
{
int i;
for( i = 1; i < file->areas.Num(); i++ )
{
file->areas[i].cluster = 0;
}
}
/*
================
idAASCluster::NumberClusterAreas
================
*/
void idAASCluster::NumberClusterAreas( int clusterNum )
{
int i, portalNum;
aasCluster_t* cluster;
aasPortal_t* portal;
cluster = &file->clusters[clusterNum];
cluster->numAreas = 0;
cluster->numReachableAreas = 0;
// number all areas in this cluster WITH reachabilities
for( i = 1; i < file->areas.Num(); i++ )
{
if( file->areas[i].cluster != clusterNum )
{
continue;
}
if( !( file->areas[i].flags & ( AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) ) )
{
continue;
}
file->areas[i].clusterAreaNum = cluster->numAreas++;
cluster->numReachableAreas++;
}
// number all portals in this cluster WITH reachabilities
for( i = 0; i < cluster->numPortals; i++ )
{
portalNum = file->portalIndex[cluster->firstPortal + i];
portal = &file->portals[portalNum];
if( !( file->areas[portal->areaNum].flags & ( AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) ) )
{
continue;
}
if( portal->clusters[0] == clusterNum )
{
portal->clusterAreaNum[0] = cluster->numAreas++;
}
else
{
portal->clusterAreaNum[1] = cluster->numAreas++;
}
cluster->numReachableAreas++;
}
// number all areas in this cluster WITHOUT reachabilities
for( i = 1; i < file->areas.Num(); i++ )
{
if( file->areas[i].cluster != clusterNum )
{
continue;
}
if( file->areas[i].flags & ( AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) )
{
continue;
}
file->areas[i].clusterAreaNum = cluster->numAreas++;
}
// number all portals in this cluster WITHOUT reachabilities
for( i = 0; i < cluster->numPortals; i++ )
{
portalNum = file->portalIndex[cluster->firstPortal + i];
portal = &file->portals[portalNum];
if( file->areas[portal->areaNum].flags & ( AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) )
{
continue;
}
if( portal->clusters[0] == clusterNum )
{
portal->clusterAreaNum[0] = cluster->numAreas++;
}
else
{
portal->clusterAreaNum[1] = cluster->numAreas++;
}
}
}
/*
================
idAASCluster::FindClusters
================
*/
bool idAASCluster::FindClusters( void )
{
int i, clusterNum;
aasCluster_t cluster;
RemoveAreaClusterNumbers();
for( i = 1; i < file->areas.Num(); i++ )
{
// if the area is already part of a cluster
if( file->areas[i].cluster )
{
continue;
}
// if not flooding through faces only use areas that have reachabilities
if( noFaceFlood )
{
if( !( file->areas[i].flags & ( AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) ) )
{
continue;
}
}
// if the area is a cluster portal
if( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL )
{
continue;
}
cluster.numAreas = 0;
cluster.numReachableAreas = 0;
cluster.firstPortal = file->portalIndex.Num();
cluster.numPortals = 0;
clusterNum = file->clusters.Num();
file->clusters.Append( cluster );
// flood the areas in this cluster
if( !FloodClusterAreas_r( i, clusterNum ) )
{
return false;
}
// number the cluster areas
NumberClusterAreas( clusterNum );
}
return true;
}
/*
================
idAASCluster::CreatePortals
================
*/
void idAASCluster::CreatePortals( void )
{
int i;
aasPortal_t portal;
for( i = 1; i < file->areas.Num(); i++ )
{
// if the area is a cluster portal
if( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL )
{
portal.areaNum = i;
portal.clusters[0] = portal.clusters[1] = 0;
portal.maxAreaTravelTime = 0;
file->portals.Append( portal );
}
}
}
/*
================
idAASCluster::TestPortals
================
*/
bool idAASCluster::TestPortals( void )
{
int i;
aasPortal_t* portal, *portal2;
aasArea_t* area, *area2;
idReachability* reach;
bool ok;
ok = true;
for( i = 1; i < file->portals.Num(); i++ )
{
portal = &file->portals[i];
area = &file->areas[portal->areaNum];
// if this portal was already removed
if( !( area->contents & AREACONTENTS_CLUSTERPORTAL ) )
{
continue;
}
// may not removed this portal if it has a reachability to a removed portal
for( reach = area->reach; reach; reach = reach->next )
{
area2 = &file->areas[ reach->toAreaNum ];
if( area2->contents & AREACONTENTS_CLUSTERPORTAL )
{
continue;
}
if( area2->cluster < 0 )
{
break;
}
}
if( reach )
{
continue;
}
// may not removed this portal if it has a reversed reachability to a removed portal
for( reach = area->rev_reach; reach; reach = reach->rev_next )
{
area2 = &file->areas[ reach->toAreaNum ];
if( area2->contents & AREACONTENTS_CLUSTERPORTAL )
{
continue;
}
if( area2->cluster < 0 )
{
break;
}
}
if( reach )
{
continue;
}
// portal should have two clusters set
if( !portal->clusters[0] )
{
area->contents &= ~AREACONTENTS_CLUSTERPORTAL;
ok = false;
continue;
}
if( !portal->clusters[1] )
{
area->contents &= ~AREACONTENTS_CLUSTERPORTAL;
ok = false;
continue;
}
// this portal may not have reachabilities to a portal that doesn't seperate the same clusters
for( reach = area->reach; reach; reach = reach->next )
{
area2 = &file->areas[ reach->toAreaNum ];
if( !( area2->contents & AREACONTENTS_CLUSTERPORTAL ) )
{
continue;
}
if( area2->cluster > 0 )
{
area2->contents &= ~AREACONTENTS_CLUSTERPORTAL;
ok = false;
continue;
}
portal2 = &file->portals[ -file->areas[ reach->toAreaNum ].cluster ];
if( ( portal2->clusters[0] != portal->clusters[0] && portal2->clusters[0] != portal->clusters[1] ) ||
( portal2->clusters[1] != portal->clusters[0] && portal2->clusters[1] != portal->clusters[1] ) )
{
area2->contents &= ~AREACONTENTS_CLUSTERPORTAL;
ok = false;
continue;
}
}
}
return ok;
}
/*
================
idAASCluster::RemoveInvalidPortals
================
*/
void idAASCluster::RemoveInvalidPortals( void )
{
int i, j, k, face1Num, face2Num, otherAreaNum, numOpenAreas, numInvalidPortals;
aasFace_t* face1, *face2;
numInvalidPortals = 0;
for( i = 0; i < file->areas.Num(); i++ )
{
if( !( file->areas[i].contents & AREACONTENTS_CLUSTERPORTAL ) )
{
continue;
}
numOpenAreas = 0;
for( j = 0; j < file->areas[i].numFaces; j++ )
{
face1Num = file->faceIndex[ file->areas[i].firstFace + j ];
face1 = &file->faces[ abs( face1Num ) ];
otherAreaNum = face1->areas[ face1Num < 0 ];
if( !otherAreaNum )
{
continue;
}
for( k = 0; k < j; k++ )
{
face2Num = file->faceIndex[ file->areas[i].firstFace + k ];
face2 = &file->faces[ abs( face2Num ) ];
if( otherAreaNum == face2->areas[ face2Num < 0 ] )
{
break;
}
}
if( k < j )
{
continue;
}
if( !( file->areas[otherAreaNum].contents & AREACONTENTS_CLUSTERPORTAL ) )
{
numOpenAreas++;
}
}
if( numOpenAreas <= 1 )
{
file->areas[i].contents &= AREACONTENTS_CLUSTERPORTAL;
numInvalidPortals++;
}
}
common->Printf( "\r%6d invalid portals removed\n", numInvalidPortals );
}
/*
================
idAASCluster::Build
================
*/
bool idAASCluster::Build( idAASFileLocal* file )
{
common->Printf( "[Clustering]\n" );
this->file = file;
this->noFaceFlood = true;
RemoveInvalidPortals();
while( 1 )
{
// delete all existing clusters
file->DeleteClusters();
// create the portals from the portal areas
CreatePortals();
common->Printf( "\r%6d", file->portals.Num() );
// find the clusters
if( !FindClusters() )
{
continue;
}
// test the portals
if( !TestPortals() )
{
continue;
}
break;
}
common->Printf( "\r%6d portals\n", file->portals.Num() );
common->Printf( "%6d clusters\n", file->clusters.Num() );
for( int i = 0; i < file->clusters.Num(); i++ )
{
common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
}
file->ReportRoutingEfficiency();
return true;
}
/*
================
idAASCluster::BuildSingleCluster
================
*/
bool idAASCluster::BuildSingleCluster( idAASFileLocal* file )
{
int i, numAreas;
aasCluster_t cluster;
common->Printf( "[Clustering]\n" );
this->file = file;
// delete all existing clusters
file->DeleteClusters();
cluster.firstPortal = 0;
cluster.numPortals = 0;
cluster.numAreas = file->areas.Num();
cluster.numReachableAreas = 0;
// give all reachable areas in the cluster a number
for( i = 0; i < file->areas.Num(); i++ )
{
file->areas[i].cluster = file->clusters.Num();
if( file->areas[i].flags & ( AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) )
{
file->areas[i].clusterAreaNum = cluster.numReachableAreas++;
}
}
// give the remaining areas a number within the cluster
numAreas = cluster.numReachableAreas;
for( i = 0; i < file->areas.Num(); i++ )
{
if( file->areas[i].flags & ( AREA_REACHABLE_WALK | AREA_REACHABLE_FLY ) )
{
continue;
}
file->areas[i].clusterAreaNum = numAreas++;
}
file->clusters.Append( cluster );
common->Printf( "%6d portals\n", file->portals.Num() );
common->Printf( "%6d clusters\n", file->clusters.Num() );
for( i = 0; i < file->clusters.Num(); i++ )
{
common->Printf( "%6d reachable areas in cluster %d\n", file->clusters[i].numReachableAreas, i );
}
file->ReportRoutingEfficiency();
return true;
}

View file

@ -0,0 +1,63 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASCLUSTER_H__
#define __AASCLUSTER_H__
/*
===============================================================================
Area Clustering
===============================================================================
*/
class idAASCluster
{
public:
bool Build( idAASFileLocal* file );
bool BuildSingleCluster( idAASFileLocal* file );
private:
idAASFileLocal* file;
bool noFaceFlood;
private:
bool UpdatePortal( int areaNum, int clusterNum );
bool FloodClusterAreas_r( int areaNum, int clusterNum );
void RemoveAreaClusterNumbers( void );
void NumberClusterAreas( int clusterNum );
bool FindClusters( void );
void CreatePortals( void );
bool TestPortals( void );
void ReportEfficiency( void );
void RemoveInvalidPortals( void );
};
#endif /* !__AASCLUSTER_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,68 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __AASREACH_H__
#define __AASREACH_H__
/*
===============================================================================
Reachabilities
===============================================================================
*/
class idAASReach
{
public:
bool Build( const idMapFile* mapFile, idAASFileLocal* file );
private:
const idMapFile* mapFile;
idAASFileLocal* file;
int numReachabilities;
bool allowSwimReachabilities;
bool allowFlyReachabilities;
private: // reachability
void FlagReachableAreas( idAASFileLocal* file );
bool ReachabilityExists( int fromAreaNum, int toAreaNum );
bool CanSwimInArea( int areaNum );
bool AreaHasFloor( int areaNum );
bool AreaIsClusterPortal( int areaNum );
void AddReachabilityToArea( idReachability* reach, int areaNum );
void Reachability_Fly( int areaNum );
void Reachability_Swim( int areaNum );
void Reachability_EqualFloorHeight( int areaNum );
bool Reachability_Step_Barrier_WaterJump_WalkOffLedge( int fromAreaNum, int toAreaNum );
void Reachability_WalkOffLedge( int areaNum );
};
#endif /* !__AASREACH_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,320 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __BRUSH_H__
#define __BRUSH_H__
/*
===============================================================================
Brushes
===============================================================================
*/
#define BRUSH_PLANESIDE_FRONT 1
#define BRUSH_PLANESIDE_BACK 2
#define BRUSH_PLANESIDE_BOTH ( BRUSH_PLANESIDE_FRONT | BRUSH_PLANESIDE_BACK )
#define BRUSH_PLANESIDE_FACING 4
class idBrush;
class idBrushList;
void DisplayRealTimeString( char* string, ... ) ID_STATIC_ATTRIBUTE_PRINTF( 1, 2 );
//===============================================================
//
// idBrushSide
//
//===============================================================
#define SFL_SPLIT 0x0001
#define SFL_BEVEL 0x0002
#define SFL_USED_SPLITTER 0x0004
#define SFL_TESTED_SPLITTER 0x0008
class idBrushSide
{
friend class idBrush;
public:
idBrushSide( void );
idBrushSide( const idPlane& plane, int planeNum );
~idBrushSide( void );
int GetFlags( void ) const
{
return flags;
}
void SetFlag( int flag )
{
flags |= flag;
}
void RemoveFlag( int flag )
{
flags &= ~flag;
}
const idPlane& GetPlane( void ) const
{
return plane;
}
void SetPlaneNum( int num )
{
planeNum = num;
}
int GetPlaneNum( void )
{
return planeNum;
}
const idWinding* GetWinding( void ) const
{
return winding;
}
idBrushSide* Copy( void ) const;
int Split( const idPlane& splitPlane, idBrushSide** front, idBrushSide** back ) const;
private:
int flags;
int planeNum;
idPlane plane;
idWinding* winding;
};
//===============================================================
//
// idBrush
//
//===============================================================
#define BFL_NO_VALID_SPLITTERS 0x0001
class idBrush
{
friend class idBrushList;
public:
idBrush( void );
~idBrush( void );
int GetFlags( void ) const
{
return flags;
}
void SetFlag( int flag )
{
flags |= flag;
}
void RemoveFlag( int flag )
{
flags &= ~flag;
}
void SetEntityNum( int num )
{
entityNum = num;
}
void SetPrimitiveNum( int num )
{
primitiveNum = num;
}
void SetContents( int contents )
{
this->contents = contents;
}
int GetContents( void ) const
{
return contents;
}
const idBounds& GetBounds( void ) const
{
return bounds;
}
float GetVolume( void ) const;
int GetNumSides( void ) const
{
return sides.Num();
}
idBrushSide* GetSide( int i ) const
{
return sides[i];
}
void SetPlaneSide( int s )
{
planeSide = s;
}
void SavePlaneSide( void )
{
savedPlaneSide = planeSide;
}
int GetSavedPlaneSide( void ) const
{
return savedPlaneSide;
}
bool FromSides( idList<idBrushSide*>& sideList );
bool FromWinding( const idWinding& w, const idPlane& windingPlane );
bool FromBounds( const idBounds& bounds );
void Transform( const idVec3& origin, const idMat3& axis );
idBrush* Copy( void ) const;
bool TryMerge( const idBrush* brush, const idPlaneSet& planeList );
// returns true if the brushes did intersect
bool Subtract( const idBrush* b, idBrushList& list ) const;
// split the brush into a front and back brush
int Split( const idPlane& plane, int planeNum, idBrush** front, idBrush** back ) const;
// expand the brush for an axial bounding box
void ExpandForAxialBox( const idBounds& bounds );
// next brush in list
idBrush* Next( void ) const
{
return next;
}
private:
mutable idBrush* next; // next brush in list
int entityNum; // entity number in editor
int primitiveNum; // primitive number in editor
int flags; // brush flags
bool windingsValid; // set when side windings are valid
int contents; // contents of brush
int planeSide; // side of a plane this brush is on
int savedPlaneSide; // saved plane side
idBounds bounds; // brush bounds
idList<idBrushSide*> sides; // list with sides
private:
bool CreateWindings( void );
void BoundBrush( const idBrush* original = NULL );
void AddBevelsForAxialBox( void );
bool RemoveSidesWithoutWinding( void );
};
//===============================================================
//
// idBrushList
//
//===============================================================
class idBrushList
{
public:
idBrushList( void );
~idBrushList( void );
int Num( void ) const
{
return numBrushes;
}
int NumSides( void ) const
{
return numBrushSides;
}
idBrush* Head( void ) const
{
return head;
}
idBrush* Tail( void ) const
{
return tail;
}
void Clear( void )
{
head = tail = NULL;
numBrushes = 0;
}
bool IsEmpty( void ) const
{
return ( numBrushes == 0 );
}
idBounds GetBounds( void ) const;
// add brush to the tail of the list
void AddToTail( idBrush* brush );
// add list to the tail of the list
void AddToTail( idBrushList& list );
// add brush to the front of the list
void AddToFront( idBrush* brush );
// add list to the front of the list
void AddToFront( idBrushList& list );
// remove the brush from the list
void Remove( idBrush* brush );
// remove the brush from the list and delete the brush
void Delete( idBrush* brush );
// returns a copy of the brush list
idBrushList* Copy( void ) const;
// delete all brushes in the list
void Free( void );
// split the brushes in the list into two lists
void Split( const idPlane& plane, int planeNum, idBrushList& frontList, idBrushList& backList, bool useBrushSavedPlaneSide = false );
// chop away all brush overlap
void Chop( bool ( *ChopAllowed )( idBrush* b1, idBrush* b2 ) );
// merge brushes
void Merge( bool ( *MergeAllowed )( idBrush* b1, idBrush* b2 ) );
// set the given flag on all brush sides facing the plane
void SetFlagOnFacingBrushSides( const idPlane& plane, int flag );
// get a list with planes for all brushes in the list
void CreatePlaneList( idPlaneSet& planeList ) const;
// write a brush map with the brushes in the list
void WriteBrushMap( const idStr& fileName, const idStr& ext ) const;
private:
idBrush* head;
idBrush* tail;
int numBrushes;
int numBrushSides;
};
//===============================================================
//
// idBrushMap
//
//===============================================================
class idBrushMap
{
public:
idBrushMap( const idStr& fileName, const idStr& ext );
~idBrushMap( void );
void SetTexture( const idStr& textureName )
{
texture = textureName;
}
void WriteBrush( const idBrush* brush );
void WriteBrushList( const idBrushList& brushList );
private:
idFile* fp;
idStr texture;
int brushCount;
};
#endif /* !__BRUSH_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,302 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __BRUSHBSP_H__
#define __BRUSHBSP_H__
/*
===============================================================================
BrushBSP
===============================================================================
*/
class idBrushBSP;
class idBrushBSPNode;
class idBrushBSPPortal;
//===============================================================
//
// idBrushBSPPortal
//
//===============================================================
class idBrushBSPPortal
{
friend class idBrushBSP;
friend class idBrushBSPNode;
public:
idBrushBSPPortal( void );
~idBrushBSPPortal( void );
void AddToNodes( idBrushBSPNode* front, idBrushBSPNode* back );
void RemoveFromNode( idBrushBSPNode* l );
void Flip( void );
int Split( const idPlane& splitPlane, idBrushBSPPortal** front, idBrushBSPPortal** back );
idWinding* GetWinding( void ) const
{
return winding;
}
const idPlane& GetPlane( void ) const
{
return plane;
}
void SetFaceNum( int num )
{
faceNum = num;
}
int GetFaceNum( void ) const
{
return faceNum;
}
int GetFlags( void ) const
{
return flags;
}
void SetFlag( int flag )
{
flags |= flag;
}
void RemoveFlag( int flag )
{
flags &= ~flag;
}
idBrushBSPPortal* Next( int side ) const
{
return next[side];
}
idBrushBSPNode* GetNode( int side ) const
{
return nodes[side];
}
private:
idPlane plane; // portal plane
int planeNum; // number of plane this portal is on
idWinding* winding; // portal winding
idBrushBSPNode* nodes[2]; // nodes this portal seperates
idBrushBSPPortal* next[2]; // next portal in list for both nodes
int flags; // portal flags
int faceNum; // number of the face created for this portal
};
//===============================================================
//
// idBrushBSPNode
//
//===============================================================
#define NODE_VISITED BIT(30)
#define NODE_DONE BIT(31)
class idBrushBSPNode
{
friend class idBrushBSP;
friend class idBrushBSPPortal;
public:
idBrushBSPNode( void );
~idBrushBSPNode( void );
void SetContentsFromBrushes( void );
idBounds GetPortalBounds( void );
idBrushBSPNode* GetChild( int index ) const
{
return children[index];
}
idBrushBSPNode* GetParent( void ) const
{
return parent;
}
void SetContents( int contents )
{
this->contents = contents;
}
int GetContents( void ) const
{
return contents;
}
const idPlane& GetPlane( void ) const
{
return plane;
}
idBrushBSPPortal* GetPortals( void ) const
{
return portals;
}
void SetAreaNum( int num )
{
areaNum = num;
}
int GetAreaNum( void ) const
{
return areaNum;
}
int GetFlags( void ) const
{
return flags;
}
void SetFlag( int flag )
{
flags |= flag;
}
void RemoveFlag( int flag )
{
flags &= ~flag;
}
bool TestLeafNode( void );
// remove the flag from nodes found by flooding through portals to nodes with the flag set
void RemoveFlagFlood( int flag );
// recurse down the tree and remove the flag from all visited nodes
void RemoveFlagRecurse( int flag );
// first recurse down the tree and flood from there
void RemoveFlagRecurseFlood( int flag );
// returns side of the plane the node is on
int PlaneSide( const idPlane& plane, float epsilon = ON_EPSILON ) const;
// split the leaf node with a plane
bool Split( const idPlane& splitPlane, int splitPlaneNum );
private:
idPlane plane; // split plane if this is not a leaf node
idBrush* volume; // node volume
int contents; // node contents
idBrushList brushList; // list with brushes for this node
idBrushBSPNode* parent; // parent of this node
idBrushBSPNode* children[2]; // both are NULL if this is a leaf node
idBrushBSPPortal* portals; // portals of this node
int flags; // node flags
int areaNum; // number of the area created for this node
int occupied; // true when portal is occupied
};
//===============================================================
//
// idBrushBSP
//
//===============================================================
class idBrushBSP
{
public:
idBrushBSP( void );
~idBrushBSP( void );
// build a bsp tree from a set of brushes
void Build( idBrushList brushList, int skipContents,
bool ( *ChopAllowed )( idBrush* b1, idBrush* b2 ),
bool ( *MergeAllowed )( idBrush* b1, idBrush* b2 ) );
// remove splits in subspaces with the given contents
void PruneTree( int contents );
// portalize the bsp tree
void Portalize( void );
// remove subspaces outside the map not reachable by entities
bool RemoveOutside( const idMapFile* mapFile, int contents, const idStrList& classNames );
// write file with a trace going through a leak
void LeakFile( const idStr& fileName );
// try to merge portals
void MergePortals( int skipContents );
// try to merge the two leaf nodes at either side of the portal
bool TryMergeLeafNodes( idBrushBSPPortal* portal, int side );
void PruneMergedTree_r( idBrushBSPNode* node );
// melt portal windings
void MeltPortals( int skipContents );
// write a map file with a brush for every leaf node that has the given contents
void WriteBrushMap( const idStr& fileName, const idStr& ext, int contents );
// bounds for the whole tree
const idBounds& GetTreeBounds( void ) const
{
return treeBounds;
}
// root node of the tree
idBrushBSPNode* GetRootNode( void ) const
{
return root;
}
private:
idBrushBSPNode* root;
idBrushBSPNode* outside;
idBounds treeBounds;
idPlaneSet portalPlanes;
int numGridCells;
int numSplits;
int numGridCellSplits;
int numPrunedSplits;
int numPortals;
int solidLeafNodes;
int outsideLeafNodes;
int insideLeafNodes;
int numMergedPortals;
int numInsertedPoints;
idVec3 leakOrigin;
int brushMapContents;
idBrushMap* brushMap;
bool ( *BrushChopAllowed )( idBrush* b1, idBrush* b2 );
bool ( *BrushMergeAllowed )( idBrush* b1, idBrush* b2 );
private:
void RemoveMultipleLeafNodeReferences_r( idBrushBSPNode* node );
void Free_r( idBrushBSPNode* node );
void IncreaseNumSplits( void );
bool IsValidSplitter( const idBrushSide* side );
int BrushSplitterStats( const idBrush* brush, int planeNum, const idPlaneSet& planeList, bool* testedPlanes, struct splitterStats_s& stats );
int FindSplitter( idBrushBSPNode* node, const idPlaneSet& planeList, bool* testedPlanes, struct splitterStats_s& bestStats );
void SetSplitterUsed( idBrushBSPNode* node, int planeNum );
idBrushBSPNode* BuildBrushBSP_r( idBrushBSPNode* node, const idPlaneSet& planeList, bool* testedPlanes, int skipContents );
idBrushBSPNode* ProcessGridCell( idBrushBSPNode* node, int skipContents );
void BuildGrid_r( idList<idBrushBSPNode*>& gridCells, idBrushBSPNode* node );
void PruneTree_r( idBrushBSPNode* node, int contents );
void MakeOutsidePortals( void );
idWinding* BaseWindingForNode( idBrushBSPNode* node );
void MakeNodePortal( idBrushBSPNode* node );
void SplitNodePortals( idBrushBSPNode* node );
void MakeTreePortals_r( idBrushBSPNode* node );
void FloodThroughPortals_r( idBrushBSPNode* node, int contents, int depth );
bool FloodFromOrigin( const idVec3& origin, int contents );
bool FloodFromEntities( const idMapFile* mapFile, int contents, const idStrList& classNames );
void RemoveOutside_r( idBrushBSPNode* node, int contents );
void SetPortalPlanes_r( idBrushBSPNode* node, idPlaneSet& planeList );
void SetPortalPlanes( void );
void MergePortals_r( idBrushBSPNode* node, int skipContents );
void MergeLeafNodePortals( idBrushBSPNode* node, int skipContents );
void UpdateTreeAfterMerge_r( idBrushBSPNode* node, const idBounds& bounds, idBrushBSPNode* oldNode, idBrushBSPNode* newNode );
void RemoveLeafNodeColinearPoints( idBrushBSPNode* node );
void RemoveColinearPoints_r( idBrushBSPNode* node, int skipContents );
void MeltFlood_r( idBrushBSPNode* node, int skipContents, idBounds& bounds, idVectorSet<idVec3, 3>& vertexList );
void MeltLeafNodePortals( idBrushBSPNode* node, int skipContents, idVectorSet<idVec3, 3>& vertexList );
void MeltPortals_r( idBrushBSPNode* node, int skipContents, idVectorSet<idVec3, 3>& vertexList );
};
#endif /* !__BRUSHBSP_H__ */

View file

@ -0,0 +1,48 @@
/*
===========================================================================
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.
===========================================================================
*/
#ifndef __COMPILER_PUBLIC_H__
#define __COMPILER_PUBLIC_H__
/*
===============================================================================
Compilers for map, aas, etc. processing.
===============================================================================
*/
// map processing (also see SuperOptimizeOccluders in tr_local.h)
void Dmap_f( const idCmdArgs& args );
// AAS file compiler
void RunAAS_f( const idCmdArgs& args );
void RunAASDir_f( const idCmdArgs& args );
void RunReach_f( const idCmdArgs& args );
#endif /* !__COMPILER_PUBLIC_H__ */

View file

@ -0,0 +1,462 @@
/*
===========================================================================
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 "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
{
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 );
// delete any old generated binary proc files
idStr generated = va( "generated/%s.bproc", dmapGlobals.mapFileBase );
fileSystem->RemoveFile( generated.c_str() );
//
// 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();
}
/*
============
Dmap_f
============
*/
void Dmap_f( const idCmdArgs& args )
{
common->ClearWarnings( "running dmap" );
// refresh the screen each time we print so it doesn't look
// like it is hung
common->SetRefreshOnPrint( true );
Dmap( args );
common->SetRefreshOnPrint( false );
common->PrintWarnings();
}

View file

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

View file

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

View file

@ -0,0 +1,320 @@
/*
===========================================================================
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 "precompiled.h"
#pragma hdrstop
#include "dmap.h"
#if 0
#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 );
GL_Set2D();
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 );
w[0] = tri->v[0].xyz;
w[1] = tri->v[1].xyz;
w[2] = tri->v[2].xyz;
GLS_Winding( &w, code );
}
void GLS_EndScene( void )
{
closesocket( draw_socket );
draw_socket = 0;
}
#else
void Draw_ClearWindow( void )
{
}
void DrawWinding( const idWinding* w )
{
}
void DrawAuxWinding( const idWinding* w )
{
}
void GLS_Winding( const idWinding* w, int code )
{
}
void GLS_BeginScene( void )
{
}
void GLS_EndScene( void )
{
}
#endif

View file

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

View file

@ -0,0 +1,113 @@
/*
===========================================================================
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 "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 = NULL;
node_t* nextnode = NULL;
int s;
// find the best portal exit
next = node->occupied;
for( p = node->portals ; p ; p = p->next[!s] )
{
s = ( p->nodes[0] == node );
if( p->nodes[s]->occupied
&& p->nodes[s]->occupied < next )
{
nextportal = p;
nextnode = p->nodes[s];
next = nextnode->occupied;
}
}
node = nextnode;
mid = nextportal->winding->GetCenter();
fprintf( linefile, "%f %f %f\n", mid[0], mid[1], mid[2] );
count++;
}
// add the occupant center
node->occupant->mapEntity->epairs.GetVector( "origin", "", mid );
fprintf( linefile, "%f %f %f\n", mid[0], mid[1], mid[2] );
common->Printf( "%5i point linefile\n", count + 1 );
fclose( linefile );
}

View file

@ -0,0 +1,676 @@
/*
===========================================================================
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 "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 ), TAG_TOOLS );
memset( prim, 0, sizeof( *prim ) );
prim->next = uEntity->primitives;
uEntity->primitives = prim;
prim->brush = b;
return 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 ), TAG_TOOLS );
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, TAG_TOOLS );
memset( dmapGlobals.uEntities, 0, size );
// allocate a very large temporary brush for building
// the brushes as they are loaded
buildBrush = AllocBrush( MAX_BUILD_SIDES );
for( i = 0 ; i < dmapGlobals.dmapFile->GetNumEntities() ; i++ )
{
ProcessMapEntity( dmapGlobals.dmapFile->GetEntity( i ) );
}
CreateMapLights( dmapGlobals.dmapFile );
brushes = 0;
triSurfs = 0;
mapBounds.Clear();
for( prim = dmapGlobals.uEntities[0].primitives ; prim ; prim = prim->next )
{
if( prim->brush )
{
brushes++;
mapBounds.AddBounds( prim->brush->bounds );
}
else if( prim->tris )
{
triSurfs++;
}
}
common->Printf( "%5i total world brushes\n", brushes );
common->Printf( "%5i total world triSurfs\n", triSurfs );
common->Printf( "%5i patches\n", c_numMapPatches );
common->Printf( "%5i entities\n", dmapGlobals.num_entities );
common->Printf( "%5i planes\n", dmapGlobals.mapPlanes.Num() );
common->Printf( "%5i areaportals\n", c_areaportals );
common->Printf( "size: %5.0f,%5.0f,%5.0f to %5.0f,%5.0f,%5.0f\n", mapBounds[0][0], mapBounds[0][1], mapBounds[0][2],
mapBounds[1][0], mapBounds[1][1], mapBounds[1][2] );
return true;
}
/*
================
FreeOptimizeGroupList
================
*/
void FreeOptimizeGroupList( optimizeGroup_t* groups )
{
optimizeGroup_t* next;
for( ; groups ; groups = next )
{
next = groups->nextGroup;
FreeTriList( groups->triList );
Mem_Free( groups );
}
}
/*
================
FreeDMapFile
================
*/
void FreeDMapFile( void )
{
int i, j;
FreeBrush( buildBrush );
buildBrush = NULL;
// free the entities and brushes
for( i = 0 ; i < dmapGlobals.num_entities ; i++ )
{
uEntity_t* ent;
primitive_t* prim, *nextPrim;
ent = &dmapGlobals.uEntities[i];
FreeTree( ent->tree );
// free primitives
for( prim = ent->primitives ; prim ; prim = nextPrim )
{
nextPrim = prim->next;
if( prim->brush )
{
FreeBrush( prim->brush );
}
if( prim->tris )
{
FreeTriList( prim->tris );
}
Mem_Free( prim );
}
// free area surfaces
if( ent->areas )
{
for( j = 0 ; j < ent->numAreas ; j++ )
{
uArea_t* area;
area = &ent->areas[j];
FreeOptimizeGroupList( area->groups );
}
Mem_Free( ent->areas );
}
}
Mem_Free( dmapGlobals.uEntities );
dmapGlobals.num_entities = 0;
// free the map lights
for( i = 0; i < dmapGlobals.mapLights.Num(); i++ )
{
R_FreeLightDefDerivedData( &dmapGlobals.mapLights[i]->def );
}
dmapGlobals.mapLights.DeleteContents( true );
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,708 @@
/*
===========================================================================
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 "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->GetTexCoordS() - b->GetTexCoordS() ) > ST_EPSILON )
{
return false;
}
if( idMath::Fabs( a->GetTexCoordT() - b->GetTexCoordT() ) > ST_EPSILON )
{
return false;
}
// if the normal is 0 (smoothed normals), consider it a match
if( a->GetNormal() == idVec3( 0, 0, 0 ) || b->GetNormal() == idVec3( 0, 0, 0 ) )
{
return true;
}
// otherwise do a dot-product cosine check
if( a->GetNormal() * b->GetNormal() < 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].SetNormal( dv->normal[0], dv->normal[1], dv->normal[2] );
uTri->verts[j].SetNormal( dv->GetNormal() );
uTri->verts[j].SetTexCoordS( dv->GetTexCoordS() );
uTri->verts[j].SetTexCoordT( dv->GetTexCoordT() );
}
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];
idVec2 st = dv->GetTexCoord();
vec[3] = st.x;
vec[4] = st.y;
idVec3 normal = dv->GetNormal();
vec[5] = normal.x;
vec[6] = normal.y;
vec[7] = normal.z;
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" );
}
}
/*
=======================
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 ), TAG_TOOLS );
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_basepath" );
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 );
}
fileSystem->CloseFile( procFile );
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,751 @@
/*
===========================================================================
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 "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 )
{
for( i = 0 ; i < 3 ; i++ )
{
int d;
d = hv->iv[i] - iv[i];
if( d < -1 || d > 1 )
{
break;
}
}
if( i == 3 )
{
v = hv->v;
return hv;
}
}
// create a new one
hv = ( hashVert_t* )Mem_Alloc( sizeof( *hv ), TAG_TOOLS );
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;
v = hv->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];
dir = v2->xyz - v1->xyz;
len = dir.Normalize();
// if it is close to one of the edge vertexes, skip it
temp = *v - v1->xyz;
d = temp * dir;
if( d <= 0 || d >= len )
{
continue;
}
// make sure it is on the line
VectorMA( v1->xyz, d, dir, temp );
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
split.xyz = *v;
frac = d / len;
idVec2 st;
st.x = v1->GetTexCoordS() + frac * ( v2->GetTexCoordS() - v1->GetTexCoordS() );
st.y = v1->GetTexCoordT() + frac * ( v2->GetTexCoordT() - v1->GetTexCoordT() );
split.SetTexCoord( st );
idVec3 splitNormal;
idVec3 v1Normal = v1->GetNormal();
idVec3 v2Normal = v2->GetNormal();
splitNormal[0] = v1Normal[0] + frac * ( v2Normal[0] - v1Normal[0] );
splitNormal[1] = v1Normal[1] + frac * ( v2Normal[1] - v1Normal[1] );
splitNormal[2] = v1Normal[2] + frac * ( v2Normal[2] - v1Normal[2] );
splitNormal.Normalize();
split.SetNormal( splitNormal );
// 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 = plane1.ToVec4() * plane2.ToVec4();
// if the two split triangle's normals don't face the same way,
// it should not be split
if( d <= 0 )
{
FreeTriList( new2 );
continue;
}
return new2;
}
return NULL;
}
/*
==================
FixTriangleAgainstHash
Potentially splits a triangle into a list of triangles based on tjunctions
==================
*/
static mapTri_t* FixTriangleAgainstHash( const mapTri_t* tri )
{
mapTri_t* fixed;
mapTri_t* a;
mapTri_t* test, *next;
int blocks[2][3];
int i, j, k;
hashVert_t* hv;
// if this triangle is degenerate after point snapping,
// do nothing (this shouldn't happen, because they should
// be removed as they are hashed)
if( tri->hashVert[0] == tri->hashVert[1]
|| tri->hashVert[0] == tri->hashVert[2]
|| tri->hashVert[1] == tri->hashVert[2] )
{
return NULL;
}
fixed = CopyMapTri( tri );
fixed->next = NULL;
HashBlocksForTri( tri, blocks );
for( i = blocks[0][0] ; i <= blocks[1][0] ; i++ )
{
for( j = blocks[0][1] ; j <= blocks[1][1] ; j++ )
{
for( k = blocks[0][2] ; k <= blocks[1][2] ; k++ )
{
for( hv = hashVerts[i][j][k] ; hv ; hv = hv->next )
{
// fix all triangles in the list against this point
test = fixed;
fixed = NULL;
for( ; test ; test = next )
{
next = test->next;
a = FixTriangleAgainstHashVert( test, hv );
if( a )
{
// cut into two triangles
a->next->next = fixed;
fixed = a;
FreeTri( test );
}
else
{
test->next = fixed;
fixed = test;
}
}
}
}
}
}
return fixed;
}
/*
==================
CountGroupListTris
==================
*/
int CountGroupListTris( const optimizeGroup_t* groupList )
{
int c;
c = 0;
for( ; groupList ; groupList = groupList->nextGroup )
{
c += CountTriList( groupList->triList );
}
return c;
}
/*
==================
FixAreaGroupsTjunctions
==================
*/
void FixAreaGroupsTjunctions( optimizeGroup_t* groupList )
{
const mapTri_t* tri;
mapTri_t* newList;
mapTri_t* fixed;
int startCount, endCount;
optimizeGroup_t* group;
if( dmapGlobals.noTJunc )
{
return;
}
if( !groupList )
{
return;
}
startCount = CountGroupListTris( groupList );
if( dmapGlobals.verbose )
{
common->Printf( "----- FixAreaGroupsTjunctions -----\n" );
common->Printf( "%6i triangles in\n", startCount );
}
HashTriangles( groupList );
for( group = groupList ; group ; group = group->nextGroup )
{
// don't touch discrete surfaces
if( group->material != NULL && group->material->IsDiscrete() )
{
continue;
}
newList = NULL;
for( tri = group->triList ; tri ; tri = tri->next )
{
fixed = FixTriangleAgainstHash( tri );
newList = MergeTriLists( newList, fixed );
}
FreeTriList( group->triList );
group->triList = newList;
}
endCount = CountGroupListTris( groupList );
if( dmapGlobals.verbose )
{
common->Printf( "%6i triangles out\n", endCount );
}
}
/*
==================
FixEntityTjunctions
==================
*/
void FixEntityTjunctions( uEntity_t* e )
{
int i;
for( i = 0 ; i < e->numAreas ; i++ )
{
FixAreaGroupsTjunctions( e->areas[i].groups );
FreeTJunctionHash();
}
}
/*
==================
FixGlobalTjunctions
==================
*/
void FixGlobalTjunctions( uEntity_t* e )
{
mapTri_t* a;
int vert;
int i;
optimizeGroup_t* group;
int areaNum;
common->Printf( "----- FixGlobalTjunctions -----\n" );
// clear the hash tables
memset( hashVerts, 0, sizeof( hashVerts ) );
numHashVerts = 0;
numTotalVerts = 0;
// bound all the triangles to determine the bucket size
hashBounds.Clear();
for( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ )
{
for( group = e->areas[areaNum].groups ; group ; group = group->nextGroup )
{
for( a = group->triList ; a ; a = a->next )
{
hashBounds.AddPoint( a->v[0].xyz );
hashBounds.AddPoint( a->v[1].xyz );
hashBounds.AddPoint( a->v[2].xyz );
}
}
}
// spread the bounds so it will never have a zero size
for( i = 0 ; i < 3 ; i++ )
{
hashBounds[0][i] = floor( hashBounds[0][i] - 1 );
hashBounds[1][i] = ceil( hashBounds[1][i] + 1 );
hashIntMins[i] = hashBounds[0][i] * SNAP_FRACTIONS;
hashScale[i] = ( hashBounds[1][i] - hashBounds[0][i] ) / HASH_BINS;
hashIntScale[i] = hashScale[i] * SNAP_FRACTIONS;
if( hashIntScale[i] < 1 )
{
hashIntScale[i] = 1;
}
}
// add all the points to the hash buckets
for( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ )
{
for( group = e->areas[areaNum].groups ; group ; group = group->nextGroup )
{
// don't touch discrete surfaces
if( group->material != NULL && group->material->IsDiscrete() )
{
continue;
}
for( a = group->triList ; a ; a = a->next )
{
for( vert = 0 ; vert < 3 ; vert++ )
{
a->hashVert[vert] = GetHashVert( a->v[vert].xyz );
}
}
}
}
// add all the func_static model vertexes to the hash buckets
// optionally inline some of the func_static models
if( dmapGlobals.entityNum == 0 )
{
for( int eNum = 1 ; eNum < dmapGlobals.num_entities ; eNum++ )
{
uEntity_t* entity = &dmapGlobals.uEntities[eNum];
const char* className = entity->mapEntity->epairs.GetString( "classname" );
if( idStr::Icmp( className, "func_static" ) )
{
continue;
}
const char* modelName = entity->mapEntity->epairs.GetString( "model" );
if( !modelName )
{
continue;
}
if( !strstr( modelName, ".lwo" ) && !strstr( modelName, ".ase" ) && !strstr( modelName, ".ma" ) )
{
continue;
}
idRenderModel* model = renderModelManager->FindModel( modelName );
// common->Printf( "adding T junction verts for %s.\n", entity->mapEntity->epairs.GetString( "name" ) );
idMat3 axis;
// get the rotation matrix in either full form, or single angle form
if( !entity->mapEntity->epairs.GetMatrix( "rotation", "1 0 0 0 1 0 0 0 1", axis ) )
{
float angle = entity->mapEntity->epairs.GetFloat( "angle" );
if( angle != 0.0f )
{
axis = idAngles( 0.0f, angle, 0.0f ).ToMat3();
}
else
{
axis.Identity();
}
}
idVec3 origin = entity->mapEntity->epairs.GetVector( "origin" );
for( i = 0 ; i < model->NumSurfaces() ; i++ )
{
const modelSurface_t* surface = model->Surface( i );
const srfTriangles_t* tri = surface->geometry;
mapTri_t mapTri;
memset( &mapTri, 0, sizeof( mapTri ) );
mapTri.material = surface->shader;
// don't let discretes (autosprites, etc) merge together
if( mapTri.material->IsDiscrete() )
{
mapTri.mergeGroup = ( void* )surface;
}
for( int j = 0 ; j < tri->numVerts ; j += 3 )
{
idVec3 v = tri->verts[j].xyz * axis + origin;
GetHashVert( v );
}
}
}
}
// now fix each area
for( areaNum = 0 ; areaNum < e->numAreas ; areaNum++ )
{
for( group = e->areas[areaNum].groups ; group ; group = group->nextGroup )
{
// don't touch discrete surfaces
if( group->material != NULL && group->material->IsDiscrete() )
{
continue;
}
mapTri_t* newList = NULL;
for( mapTri_t* tri = group->triList ; tri ; tri = tri->next )
{
mapTri_t* fixed = FixTriangleAgainstHash( tri );
newList = MergeTriLists( newList, fixed );
}
FreeTriList( group->triList );
group->triList = newList;
}
}
// done
FreeTJunctionHash();
}

View file

@ -0,0 +1,428 @@
/*
===========================================================================
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 "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 ), TAG_TOOLS );
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 ), TAG_TOOLS );
*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 );
w[0] = tri->v[0].xyz;
w[1] = tri->v[1].xyz;
w[2] = tri->v[2].xyz;
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 );
( *w )[0] = tri->v[0].xyz;
( *w )[1] = tri->v[1].xyz;
( *w )[2] = tri->v[2].xyz;
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].SetTexCoordS( a * original->v[0].GetTexCoordS()
+ b * original->v[1].GetTexCoordS() + c * original->v[2].GetTexCoordS() );
tri->v[i].SetTexCoordT( a * original->v[0].GetTexCoordT()
+ b * original->v[1].GetTexCoordT() + c * original->v[2].GetTexCoordT() );
idVec3 normal = tri->v[i].GetNormal();
for( j = 0 ; j < 3 ; j++ )
{
normal[j] = a * original->v[0].GetNormal()[j]
+ b * original->v[1].GetNormal()[j] + c * original->v[2].GetNormal()[j];
}
normal.Normalize();
tri->v[i].SetNormal( normal );
}
}
/*
================
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();
}
tri->v[j].xyz = *vec;
}
if( originalTri )
{
TriVertsFromOriginal( tri, originalTri );
}
}
return triList;
}
/*
==================
ClipTriList
==================
*/
void ClipTriList( const mapTri_t* list, const idPlane& plane, float epsilon,
mapTri_t** front, mapTri_t** back )
{
const mapTri_t* tri;
mapTri_t* newList;
idWinding* w, *frontW, *backW;
*front = NULL;
*back = NULL;
for( tri = list ; tri ; tri = tri->next )
{
w = WindingForTri( tri );
w->Split( plane, epsilon, &frontW, &backW );
newList = WindingToTriList( frontW, tri );
*front = MergeTriLists( *front, newList );
newList = WindingToTriList( backW, tri );
*back = MergeTriLists( *back, newList );
delete w;
}
}
/*
==================
PlaneForTri
==================
*/
void PlaneForTri( const mapTri_t* tri, idPlane& plane )
{
plane.FromPoints( tri->v[0].xyz, tri->v[1].xyz, tri->v[2].xyz );
}

View file

@ -0,0 +1,753 @@
/*
===========================================================================
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 "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, TAG_TOOLS );
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;
}
corner = ( *w )[0].ToVec3();
// 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 ), TAG_TOOLS );
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 ), TAG_TOOLS );
memset( node, 0, sizeof( *node ) );
return node;
}
//============================================================
/*
==================
BrushMostlyOnSide
==================
*/
int BrushMostlyOnSide( uBrush_t* brush, idPlane& plane )
{
int i, j;
idWinding* w;
float d, max;
int side;
max = 0;
side = PSIDE_FRONT;
for( i = 0; i < brush->numsides; i++ )
{
w = brush->sides[i].winding;
if( !w )
continue;
for( j = 0; j < w->GetNumPoints(); j++ )
{
d = plane.Distance( ( *w )[j].ToVec3() );
if( d > max )
{
max = d;
side = PSIDE_FRONT;
}
if( -d > max )
{
max = -d;
side = PSIDE_BACK;
}
}
}
return side;
}
/*
================
SplitBrush
Generates two new brushes, leaving the original
unchanged
================
*/
void SplitBrush( uBrush_t* brush, int planenum, uBrush_t** front, uBrush_t** back )
{
uBrush_t* b[2];
int i, j;
idWinding* w, *cw[2], *midwinding;
side_t* s, *cs;
float d, d_front, d_back;
*front = *back = NULL;
idPlane& plane = dmapGlobals.mapPlanes[planenum];
// check all points
d_front = d_back = 0;
for( i = 0; i < brush->numsides; i++ )
{
w = brush->sides[i].winding;
if( !w )
{
continue;
}
for( j = 0; j < w->GetNumPoints(); j++ )
{
d = plane.Distance( ( *w )[j].ToVec3() );
if( d > 0 && d > d_front )
d_front = d;
if( d < 0 && d < d_back )
d_back = d;
}
}
if( d_front < 0.1 ) // PLANESIDE_EPSILON)
{
// only on back
*back = CopyBrush( brush );
return;
}
if( d_back > -0.1 ) // PLANESIDE_EPSILON)
{
// only on front
*front = CopyBrush( brush );
return;
}
// create a new winding from the split plane
w = new idWinding( plane );
for( i = 0; i < brush->numsides && w; i++ )
{
idPlane& plane2 = dmapGlobals.mapPlanes[brush->sides[i].planenum ^ 1];
w = w->Clip( plane2, 0 ); // PLANESIDE_EPSILON);
}
if( !w || w->IsTiny() )
{
// the brush isn't really split
int side;
side = BrushMostlyOnSide( brush, plane );
if( side == PSIDE_FRONT )
*front = CopyBrush( brush );
if( side == PSIDE_BACK )
*back = CopyBrush( brush );
return;
}
if( w->IsHuge() )
{
common->Printf( "WARNING: huge winding\n" );
}
midwinding = w;
// split it for real
for( i = 0; i < 2; i++ )
{
b[i] = AllocBrush( brush->numsides + 1 );
memcpy( b[i], brush, sizeof( uBrush_t ) - sizeof( brush->sides ) );
b[i]->numsides = 0;
b[i]->next = NULL;
b[i]->original = brush->original;
}
// split all the current windings
for( i = 0; i < brush->numsides; i++ )
{
s = &brush->sides[i];
w = s->winding;
if( !w )
continue;
w->Split( plane, 0 /*PLANESIDE_EPSILON*/, &cw[0], &cw[1] );
for( j = 0; j < 2; j++ )
{
if( !cw[j] )
{
continue;
}
/*
if ( cw[j]->IsTiny() )
{
delete cw[j];
continue;
}
*/
cs = &b[j]->sides[b[j]->numsides];
b[j]->numsides++;
*cs = *s;
cs->winding = cw[j];
}
}
// see if we have valid polygons on both sides
for( i = 0 ; i < 2 ; i++ )
{
if( !BoundBrush( b[i] ) )
{
break;
}
if( b[i]->numsides < 3 )
{
FreeBrush( b[i] );
b[i] = NULL;
}
}
if( !( b[0] && b[1] ) )
{
if( !b[0] && !b[1] )
common->Printf( "split removed brush\n" );
else
common->Printf( "split not on both sides\n" );
if( b[0] )
{
FreeBrush( b[0] );
*front = CopyBrush( brush );
}
if( b[1] )
{
FreeBrush( b[1] );
*back = CopyBrush( brush );
}
return;
}
// add the midwinding to both sides
for( i = 0 ; i < 2 ; i++ )
{
cs = &b[i]->sides[b[i]->numsides];
b[i]->numsides++;
cs->planenum = planenum ^ i ^ 1;
cs->material = NULL;
if( i == 0 )
cs->winding = midwinding->Copy();
else
cs->winding = midwinding;
}
{
float v1;
int i;
for( i = 0 ; i < 2 ; i++ )
{
v1 = BrushVolume( b[i] );
if( v1 < 1.0 )
{
FreeBrush( b[i] );
b[i] = NULL;
// common->Printf ("tiny volume after clip\n");
}
}
}
*front = b[0];
*back = b[1];
}

File diff suppressed because it is too large Load diff