mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-15 07:40:44 +02:00
dmap now contains path tracing optimizations stored in iceproc files.
This commit is contained in:
@@ -151,6 +151,7 @@ void DmapHelp( void ) {
|
||||
"Usage: dmap [options] mapfile\n"
|
||||
"Options:\n"
|
||||
"noCurves = don't process curves\n"
|
||||
"noAutoAreaRefine = don't add generated optimization portals inside areas\n"
|
||||
"noCM = don't create collision map\n"
|
||||
"noAAS = don't create AAS files\n"
|
||||
|
||||
@@ -163,6 +164,7 @@ ResetDmapGlobals
|
||||
============
|
||||
*/
|
||||
void ResetDmapGlobals( void ) {
|
||||
ClearInterAreaPortals();
|
||||
dmapGlobals.mapFileBase[0] = '\0';
|
||||
dmapGlobals.dmapFile = NULL;
|
||||
dmapGlobals.mapPlanes.Clear();
|
||||
@@ -180,6 +182,7 @@ void ResetDmapGlobals( void ) {
|
||||
dmapGlobals.noTJunc = false;
|
||||
dmapGlobals.nomerge = false;
|
||||
dmapGlobals.noFlood = false;
|
||||
dmapGlobals.autoAreaRefine = true;
|
||||
dmapGlobals.noClipSides = false;
|
||||
dmapGlobals.noLightCarve = false;
|
||||
dmapGlobals.noShadow = false;
|
||||
@@ -243,6 +246,12 @@ void Dmap( const idCmdArgs &args ) {
|
||||
} else if ( !idStr::Icmp( s, "noFlood" ) ) {
|
||||
common->Printf( "noFlood = true\n" );
|
||||
dmapGlobals.noFlood = true;
|
||||
} else if ( !idStr::Icmp( s, "autoAreaRefine" ) ) {
|
||||
common->Printf( "autoAreaRefine = true\n" );
|
||||
dmapGlobals.autoAreaRefine = true;
|
||||
} else if ( !idStr::Icmp( s, "noAutoAreaRefine" ) ) {
|
||||
common->Printf( "autoAreaRefine = false\n" );
|
||||
dmapGlobals.autoAreaRefine = false;
|
||||
} else if ( !idStr::Icmp( s, "noLightCarve" ) ) {
|
||||
common->Printf( "noLightCarve = true\n" );
|
||||
dmapGlobals.noLightCarve = true;
|
||||
@@ -375,6 +384,8 @@ void Dmap( const idCmdArgs &args ) {
|
||||
}
|
||||
}
|
||||
|
||||
ClearInterAreaPortals();
|
||||
|
||||
// free the common .map representation
|
||||
delete dmapGlobals.dmapFile;
|
||||
|
||||
|
||||
@@ -153,6 +153,8 @@ typedef struct node_s {
|
||||
// needed for FindSideForPortal
|
||||
|
||||
int area; // determined by flood filling up to areaportals
|
||||
int originalArea; // area before automatic refinement
|
||||
int areaRefineFlood; // temporary flood marker for automatic refinement
|
||||
int occupied; // 1 or greater can reach entity
|
||||
uEntity_t * occupant; // for leak file testing
|
||||
|
||||
@@ -256,6 +258,7 @@ typedef struct {
|
||||
bool noTJunc;
|
||||
bool nomerge;
|
||||
bool noFlood;
|
||||
bool autoAreaRefine; // split designer-authored areas with generated BSP portals
|
||||
bool noClipSides; // don't cut sides by solid leafs, use the entire thing
|
||||
bool noLightCarve; // extra triangle subdivision by light frustums
|
||||
shadowOptLevel_t shadowOptLevel;
|
||||
@@ -339,6 +342,8 @@ void GLS_EndScene( void );
|
||||
typedef struct {
|
||||
int area0, area1;
|
||||
side_t *side;
|
||||
idWinding *winding; // generated portals don't have a map brush side
|
||||
bool generated;
|
||||
} interAreaPortal_t;
|
||||
|
||||
extern interAreaPortal_t interAreaPortals[MAX_INTER_AREA_PORTALS];
|
||||
@@ -348,6 +353,7 @@ bool FloodEntities( tree_t *tree );
|
||||
void FillOutside( uEntity_t *e );
|
||||
void FloodAreas( uEntity_t *e );
|
||||
void MakeTreePortals( tree_t *tree );
|
||||
void ClearInterAreaPortals( void );
|
||||
void FreePortal( uPortal_t *p );
|
||||
|
||||
//=============================================================================
|
||||
|
||||
@@ -310,6 +310,134 @@ static void WriteUTriangles( const srfTriangles_t *uTris ) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetImageAverageColorForProc( idImage *image, float averageColor[4] ) {
|
||||
if ( !image ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte *pic = NULL;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
textureDepth_t depth = TD_DEFAULT;
|
||||
|
||||
// Do not call idImage::ActuallyLoadImage() from dmap. dmap can run inside
|
||||
// the live game process, and ActuallyLoadImage() mutates GL/D3D texture
|
||||
// objects. Read the source pixels directly so metadata generation is pure.
|
||||
R_LoadImageProgram( image->imgName, &pic, &width, &height, NULL, &depth );
|
||||
if ( !pic || width <= 0 || height <= 0 ) {
|
||||
if ( pic ) {
|
||||
R_StaticFree( pic );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t sum[4] = { 0, 0, 0, 0 };
|
||||
const int pixelCount = width * height;
|
||||
for ( int i = 0 ; i < pixelCount ; i++ ) {
|
||||
const byte *rgba = pic + i * 4;
|
||||
sum[0] += rgba[0];
|
||||
sum[1] += rgba[1];
|
||||
sum[2] += rgba[2];
|
||||
sum[3] += rgba[3];
|
||||
}
|
||||
R_StaticFree( pic );
|
||||
|
||||
const float scale = 1.0f / ( 255.0f * (float)pixelCount );
|
||||
averageColor[0] = idMath::ClampFloat( 0.0f, 1.0f, sum[0] * scale );
|
||||
averageColor[1] = idMath::ClampFloat( 0.0f, 1.0f, sum[1] * scale );
|
||||
averageColor[2] = idMath::ClampFloat( 0.0f, 1.0f, sum[2] * scale );
|
||||
averageColor[3] = idMath::ClampFloat( 0.0f, 1.0f, sum[3] * scale );
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetVertexAverageColorForProc( const srfTriangles_t *uTris, float averageColor[4] ) {
|
||||
float sum[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
|
||||
if ( !uTris || uTris->numVerts <= 0 ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( int i = 0 ; i < uTris->numVerts ; i++ ) {
|
||||
sum[0] += uTris->verts[i].color[0] * ( 1.0f / 255.0f );
|
||||
sum[1] += uTris->verts[i].color[1] * ( 1.0f / 255.0f );
|
||||
sum[2] += uTris->verts[i].color[2] * ( 1.0f / 255.0f );
|
||||
sum[3] += uTris->verts[i].color[3] * ( 1.0f / 255.0f );
|
||||
}
|
||||
|
||||
const float invNumVerts = 1.0f / uTris->numVerts;
|
||||
averageColor[0] = sum[0] * invNumVerts;
|
||||
averageColor[1] = sum[1] * invNumVerts;
|
||||
averageColor[2] = sum[2] * invNumVerts;
|
||||
averageColor[3] = sum[3] * invNumVerts;
|
||||
|
||||
return averageColor[0] > 0.0f || averageColor[1] > 0.0f || averageColor[2] > 0.0f || averageColor[3] > 0.0f;
|
||||
}
|
||||
|
||||
static void WriteRaytraceSurfaceMetadata( const idMaterial *material, const srfTriangles_t *uTris, int areaNum ) {
|
||||
float averageColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
float emissiveColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
float bounds[6];
|
||||
uint32_t flags = 0;
|
||||
|
||||
if ( material ) {
|
||||
idImage *diffuseImage = material->GetDiffuseImage( NULL );
|
||||
if ( !GetImageAverageColorForProc( diffuseImage, averageColor ) ) {
|
||||
GetVertexAverageColorForProc( uTris, averageColor );
|
||||
}
|
||||
|
||||
idImage *glowImage = material->GetGlowImage( NULL );
|
||||
if ( !glowImage ) {
|
||||
for ( int stageNum = 0 ; stageNum < material->GetNumStages() ; stageNum++ ) {
|
||||
const shaderStage_t *stage = material->GetStage( stageNum );
|
||||
if ( stage && stage->lighting == SL_GLOWMAP && stage->texture.image ) {
|
||||
glowImage = stage->texture.image;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( glowImage ) {
|
||||
GetImageAverageColorForProc( glowImage, emissiveColor );
|
||||
emissiveColor[3] = Max( Max( emissiveColor[0], emissiveColor[1] ), emissiveColor[2] ) > 0.01f ? 1.0f : 0.0f;
|
||||
if ( emissiveColor[3] > 0.0f ) {
|
||||
flags |= GL_RAYTRACING_PROC_SURFACE_EMISSIVE;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !material->IsLitMaterial() ) {
|
||||
flags |= GL_RAYTRACING_PROC_SURFACE_UNLIT;
|
||||
}
|
||||
if ( material->IsSky() ) {
|
||||
flags |= GL_RAYTRACING_PROC_SURFACE_SKY;
|
||||
}
|
||||
if ( material->Coverage() == MC_PERFORATED ) {
|
||||
flags |= GL_RAYTRACING_PROC_SURFACE_ALPHA_TESTED;
|
||||
}
|
||||
if ( material->GetCullType() == CT_TWO_SIDED ) {
|
||||
flags |= GL_RAYTRACING_PROC_SURFACE_TWO_SIDED;
|
||||
}
|
||||
}
|
||||
|
||||
idBounds triBounds;
|
||||
triBounds.Clear();
|
||||
for ( int i = 0 ; i < uTris->numVerts ; i++ ) {
|
||||
triBounds.AddPoint( uTris->verts[i].xyz );
|
||||
}
|
||||
bounds[0] = triBounds[0][0];
|
||||
bounds[1] = triBounds[0][1];
|
||||
bounds[2] = triBounds[0][2];
|
||||
bounds[3] = triBounds[1][0];
|
||||
bounds[4] = triBounds[1][1];
|
||||
bounds[5] = triBounds[1][2];
|
||||
|
||||
procFile->WriteFloatString( "raytraceSurface { flags %u area %i averageColor ", flags, areaNum );
|
||||
Write1DMatrix( procFile, 4, averageColor );
|
||||
procFile->WriteFloatString( " emissive " );
|
||||
Write1DMatrix( procFile, 4, emissiveColor );
|
||||
procFile->WriteFloatString( " bounds " );
|
||||
Write1DMatrix( procFile, 6, bounds );
|
||||
procFile->WriteFloatString( " }\n" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
@@ -478,13 +606,15 @@ typedef struct interactionTris_s {
|
||||
|
||||
procFile->WriteFloatString( "/* surface %i */ { ", surfaceNum );
|
||||
surfaceNum++;
|
||||
procFile->WriteFloatString( "\"%s\" ", ambient->material->GetName() );
|
||||
const idMaterial *surfaceMaterial = ambient->material;
|
||||
procFile->WriteFloatString( "\"%s\" ", surfaceMaterial->GetName() );
|
||||
|
||||
uTri = ShareMapTriVerts( ambient );
|
||||
FreeTriList( ambient );
|
||||
|
||||
CleanupUTriangles( uTri );
|
||||
WriteUTriangles( uTri );
|
||||
WriteRaytraceSurfaceMetadata( surfaceMaterial, uTri, areaNum );
|
||||
R_FreeStaticTriSurf( uTri );
|
||||
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
@@ -583,7 +713,16 @@ static void WriteOutputPortals( uEntity_t *e ) {
|
||||
procFile->WriteFloatString( "/* interAreaPortal format is: numPoints positiveSideArea negativeSideArea ( point) ... */\n" );
|
||||
for ( i = 0 ; i < numInterAreaPortals ; i++ ) {
|
||||
iap = &interAreaPortals[i];
|
||||
w = iap->side->winding;
|
||||
if ( iap->winding ) {
|
||||
w = iap->winding;
|
||||
} else if ( iap->side && iap->side->visibleHull ) {
|
||||
w = iap->side->visibleHull;
|
||||
} else if ( iap->side ) {
|
||||
w = iap->side->winding;
|
||||
} else {
|
||||
common->Error( "WriteOutputPortals: inter-area portal without a winding" );
|
||||
continue;
|
||||
}
|
||||
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() );
|
||||
@@ -594,6 +733,88 @@ static void WriteOutputPortals( uEntity_t *e ) {
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
WritePathTraceAreas
|
||||
|
||||
Writes deterministic per-area data for path-tracing light culling.
|
||||
This is deliberately independent of authored light entities so dynamic lights
|
||||
can use the same area table at runtime.
|
||||
====================
|
||||
*/
|
||||
static void WritePathTraceAreas( uEntity_t *e ) {
|
||||
procFile->WriteFloatString( "pathTraceAreas { /* numAreas = */ %i\n\n", e->numAreas );
|
||||
procFile->WriteFloatString( "/* pathTraceArea format is: areaNum surfaceGroups ( bounds[6] ) neighborCount neighborArea ... lightCount \"lightName\" ... */\n" );
|
||||
|
||||
for ( int areaNum = 0 ; areaNum < e->numAreas ; areaNum++ ) {
|
||||
uArea_t *area = &e->areas[areaNum];
|
||||
idBounds bounds;
|
||||
int surfaceGroups = 0;
|
||||
idList<int> neighbors;
|
||||
idList<mapLight_t *> lights;
|
||||
|
||||
bounds.Clear();
|
||||
|
||||
for ( optimizeGroup_t *group = area->groups ; group ; group = group->nextGroup ) {
|
||||
if ( !group->triList ) {
|
||||
continue;
|
||||
}
|
||||
surfaceGroups++;
|
||||
bounds.AddPoint( group->bounds[0] );
|
||||
bounds.AddPoint( group->bounds[1] );
|
||||
|
||||
for ( int lightNum = 0 ; lightNum < group->numGroupLights ; lightNum++ ) {
|
||||
mapLight_t *light = group->groupLights[lightNum];
|
||||
if ( light && lights.FindIndex( light ) < 0 ) {
|
||||
lights.Append( light );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( bounds[0][0] > bounds[1][0] ) {
|
||||
if ( e->tree ) {
|
||||
bounds = e->tree->bounds;
|
||||
} else {
|
||||
bounds.Zero();
|
||||
}
|
||||
}
|
||||
|
||||
for ( int portalNum = 0 ; portalNum < numInterAreaPortals ; portalNum++ ) {
|
||||
const interAreaPortal_t *iap = &interAreaPortals[portalNum];
|
||||
int neighbor = -1;
|
||||
|
||||
if ( iap->area0 == areaNum ) {
|
||||
neighbor = iap->area1;
|
||||
} else if ( iap->area1 == areaNum ) {
|
||||
neighbor = iap->area0;
|
||||
}
|
||||
|
||||
if ( neighbor >= 0 && neighbors.FindIndex( neighbor ) < 0 ) {
|
||||
neighbors.Append( neighbor );
|
||||
}
|
||||
}
|
||||
|
||||
float boundsVec[6] = {
|
||||
bounds[0][0], bounds[0][1], bounds[0][2],
|
||||
bounds[1][0], bounds[1][1], bounds[1][2]
|
||||
};
|
||||
|
||||
procFile->WriteFloatString( "/* area %i */ %i %i ", areaNum, areaNum, surfaceGroups );
|
||||
Write1DMatrix( procFile, 6, boundsVec );
|
||||
procFile->WriteFloatString( "%i ", neighbors.Num() );
|
||||
for ( int i = 0 ; i < neighbors.Num() ; i++ ) {
|
||||
procFile->WriteFloatString( "%i ", neighbors[i] );
|
||||
}
|
||||
procFile->WriteFloatString( "%i ", lights.Num() );
|
||||
for ( int i = 0 ; i < lights.Num() ; i++ ) {
|
||||
procFile->WriteFloatString( "\"%s\" ", lights[i]->name );
|
||||
}
|
||||
procFile->WriteFloatString( "\n" );
|
||||
}
|
||||
|
||||
procFile->WriteFloatString( "}\n\n" );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
====================
|
||||
@@ -625,6 +846,10 @@ static void WriteOutputEntity( int entityNum ) {
|
||||
// output the nodes
|
||||
WriteOutputNodes( e->tree->headnode );
|
||||
}
|
||||
|
||||
if ( entityNum == 0 ) {
|
||||
WritePathTraceAreas( e );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,33 @@ int numInterAreaPortals;
|
||||
int c_active_portals;
|
||||
int c_peak_portals;
|
||||
|
||||
static int c_autoAreaRefineSplits;
|
||||
static int c_autoAreaRefinePortals;
|
||||
static int c_autoAreaRefineFlood;
|
||||
|
||||
#define AUTO_AREA_REFINE_MIN_LEAFS 12
|
||||
#define AUTO_AREA_REFINE_MIN_SPLIT_LEAFS 4
|
||||
#define AUTO_AREA_REFINE_MAX_SPLITS 256
|
||||
#define AUTO_AREA_REFINE_MIN_PORTAL_AREA 64.0f
|
||||
#define AUTO_AREA_REFINE_MAX_PORTAL_AREA 65536.0f
|
||||
|
||||
/*
|
||||
=============
|
||||
ClearInterAreaPortals
|
||||
=============
|
||||
*/
|
||||
void ClearInterAreaPortals( void ) {
|
||||
for ( int i = 0 ; i < numInterAreaPortals ; i++ ) {
|
||||
if ( interAreaPortals[i].generated && interAreaPortals[i].winding ) {
|
||||
delete interAreaPortals[i].winding;
|
||||
}
|
||||
interAreaPortals[i].side = NULL;
|
||||
interAreaPortals[i].winding = NULL;
|
||||
interAreaPortals[i].generated = false;
|
||||
}
|
||||
numInterAreaPortals = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
AllocPortal
|
||||
@@ -824,10 +851,244 @@ void ClearAreas_r( node_t *node ) {
|
||||
return;
|
||||
}
|
||||
node->area = -1;
|
||||
node->originalArea = -1;
|
||||
node->areaRefineFlood = 0;
|
||||
}
|
||||
|
||||
//=============================================================
|
||||
|
||||
static void MarkOriginalAreas_r( node_t *node ) {
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
MarkOriginalAreas_r( node->children[0] );
|
||||
MarkOriginalAreas_r( node->children[1] );
|
||||
return;
|
||||
}
|
||||
node->originalArea = node->area;
|
||||
}
|
||||
|
||||
static void CollectAreaLeaves_r( node_t *node, int area, idList<node_t *> &leaves ) {
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
CollectAreaLeaves_r( node->children[0], area, leaves );
|
||||
CollectAreaLeaves_r( node->children[1], area, leaves );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !node->opaque && node->area == area ) {
|
||||
leaves.Append( node );
|
||||
}
|
||||
}
|
||||
|
||||
static void FloodAreaComponent_r( node_t *node, int area, const uPortal_t *blockedPortal, int floodNum, idList<node_t *> &component ) {
|
||||
uPortal_t *p;
|
||||
int s;
|
||||
|
||||
if ( node->planenum != PLANENUM_LEAF || node->opaque || node->area != area ) {
|
||||
return;
|
||||
}
|
||||
if ( node->areaRefineFlood == floodNum ) {
|
||||
return;
|
||||
}
|
||||
|
||||
node->areaRefineFlood = floodNum;
|
||||
component.Append( node );
|
||||
|
||||
for ( p = node->portals ; p ; p = p->next[s] ) {
|
||||
s = ( p->nodes[1] == node );
|
||||
|
||||
if ( p == blockedPortal ) {
|
||||
continue;
|
||||
}
|
||||
if ( !Portal_Passable( p ) ) {
|
||||
continue;
|
||||
}
|
||||
FloodAreaComponent_r( p->nodes[!s], area, blockedPortal, floodNum, component );
|
||||
}
|
||||
}
|
||||
|
||||
static uPortal_t *FindBestAutoAreaPortal( int area, const idList<node_t *> &leaves ) {
|
||||
uPortal_t *bestPortal;
|
||||
float bestScore;
|
||||
|
||||
bestPortal = NULL;
|
||||
bestScore = 0.0f;
|
||||
|
||||
for ( int i = 0 ; i < leaves.Num() ; i++ ) {
|
||||
node_t *node = leaves[i];
|
||||
int s;
|
||||
|
||||
for ( uPortal_t *p = node->portals ; p ; p = p->next[s] ) {
|
||||
s = ( p->nodes[1] == node );
|
||||
|
||||
if ( p->nodes[0] != node ) {
|
||||
continue; // evaluate each portal once
|
||||
}
|
||||
if ( !Portal_Passable( p ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( p->nodes[1]->area != area ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float portalArea = p->winding->GetArea();
|
||||
if ( portalArea < AUTO_AREA_REFINE_MIN_PORTAL_AREA || portalArea > AUTO_AREA_REFINE_MAX_PORTAL_AREA ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
idList<node_t *> component;
|
||||
FloodAreaComponent_r( p->nodes[0], area, p, ++c_autoAreaRefineFlood, component );
|
||||
|
||||
int frontLeafs = component.Num();
|
||||
int backLeafs = leaves.Num() - frontLeafs;
|
||||
if ( frontLeafs < AUTO_AREA_REFINE_MIN_SPLIT_LEAFS || backLeafs < AUTO_AREA_REFINE_MIN_SPLIT_LEAFS ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int smaller = frontLeafs < backLeafs ? frontLeafs : backLeafs;
|
||||
int larger = frontLeafs > backLeafs ? frontLeafs : backLeafs;
|
||||
float balance = (float)smaller / (float)larger;
|
||||
float score = balance * 100000.0f / portalArea;
|
||||
|
||||
if ( score > bestScore ) {
|
||||
bestScore = score;
|
||||
bestPortal = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestPortal;
|
||||
}
|
||||
|
||||
static bool SplitAreaWithGeneratedPortal( uEntity_t *e, int area ) {
|
||||
idList<node_t *> leaves;
|
||||
idList<node_t *> component;
|
||||
|
||||
CollectAreaLeaves_r( e->tree->headnode, area, leaves );
|
||||
if ( leaves.Num() < AUTO_AREA_REFINE_MIN_LEAFS ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uPortal_t *portal = FindBestAutoAreaPortal( area, leaves );
|
||||
if ( !portal ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int floodNum = ++c_autoAreaRefineFlood;
|
||||
FloodAreaComponent_r( portal->nodes[0], area, portal, floodNum, component );
|
||||
if ( component.Num() <= 0 || component.Num() >= leaves.Num() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int newArea = e->numAreas;
|
||||
e->numAreas++;
|
||||
|
||||
// Keep the larger partition on the original area number so repeated
|
||||
// refinement tends to continue from the broadest remaining space.
|
||||
if ( component.Num() <= leaves.Num() / 2 ) {
|
||||
for ( int i = 0 ; i < component.Num() ; i++ ) {
|
||||
component[i]->area = newArea;
|
||||
}
|
||||
} else {
|
||||
for ( int i = 0 ; i < leaves.Num() ; i++ ) {
|
||||
if ( leaves[i]->areaRefineFlood != floodNum ) {
|
||||
leaves[i]->area = newArea;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c_autoAreaRefineSplits++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void AutoRefineAreas( uEntity_t *e ) {
|
||||
int startingAreas;
|
||||
|
||||
if ( !dmapGlobals.autoAreaRefine ) {
|
||||
return;
|
||||
}
|
||||
if ( e != &dmapGlobals.uEntities[0] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
startingAreas = e->numAreas;
|
||||
c_autoAreaRefineSplits = 0;
|
||||
c_autoAreaRefinePortals = 0;
|
||||
c_autoAreaRefineFlood = 0;
|
||||
|
||||
for ( int area = 0 ; area < e->numAreas && c_autoAreaRefineSplits < AUTO_AREA_REFINE_MAX_SPLITS ; area++ ) {
|
||||
while ( c_autoAreaRefineSplits < AUTO_AREA_REFINE_MAX_SPLITS ) {
|
||||
if ( !SplitAreaWithGeneratedPortal( e, area ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( c_autoAreaRefineSplits > 0 ) {
|
||||
common->Printf( "%5i auto area refinement splits (%i -> %i areas)\n", c_autoAreaRefineSplits, startingAreas, e->numAreas );
|
||||
} else {
|
||||
common->Printf( "%5i auto area refinement splits\n", 0 );
|
||||
}
|
||||
}
|
||||
|
||||
static void AddGeneratedInterAreaPortal( uPortal_t *p ) {
|
||||
if ( numInterAreaPortals >= MAX_INTER_AREA_PORTALS ) {
|
||||
common->Warning( "MAX_INTER_AREA_PORTALS hit while adding generated area portals" );
|
||||
return;
|
||||
}
|
||||
if ( !p->winding || p->winding->IsTiny() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
interAreaPortal_t *iap = &interAreaPortals[numInterAreaPortals];
|
||||
numInterAreaPortals++;
|
||||
|
||||
iap->area0 = p->nodes[0]->area;
|
||||
iap->area1 = p->nodes[1]->area;
|
||||
iap->side = NULL;
|
||||
iap->winding = p->winding->Copy();
|
||||
iap->generated = true;
|
||||
c_autoAreaRefinePortals++;
|
||||
}
|
||||
|
||||
static void FindGeneratedInterAreaPortals_r( node_t *node ) {
|
||||
uPortal_t *p;
|
||||
int s;
|
||||
|
||||
if ( node->planenum != PLANENUM_LEAF ) {
|
||||
FindGeneratedInterAreaPortals_r( node->children[0] );
|
||||
FindGeneratedInterAreaPortals_r( node->children[1] );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( node->opaque ) {
|
||||
return;
|
||||
}
|
||||
|
||||
for ( p = node->portals ; p ; p = p->next[s] ) {
|
||||
node_t *other;
|
||||
|
||||
s = ( p->nodes[1] == node );
|
||||
other = p->nodes[!s];
|
||||
|
||||
if ( other->opaque ) {
|
||||
continue;
|
||||
}
|
||||
if ( !Portal_Passable( p ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( other->area <= node->area ) {
|
||||
continue;
|
||||
}
|
||||
if ( other->originalArea != node->originalArea ) {
|
||||
continue; // authored areaportal boundary
|
||||
}
|
||||
if ( FindSideForPortal( p ) ) {
|
||||
continue; // designer-authored portal, already emitted
|
||||
}
|
||||
|
||||
AddGeneratedInterAreaPortal( p );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
@@ -895,6 +1156,11 @@ static void FindInterAreaPortals_r( node_t *node ) {
|
||||
continue; // already emited
|
||||
}
|
||||
|
||||
if ( numInterAreaPortals >= MAX_INTER_AREA_PORTALS ) {
|
||||
common->Warning( "MAX_INTER_AREA_PORTALS hit while adding designer area portals" );
|
||||
continue;
|
||||
}
|
||||
|
||||
iap = &interAreaPortals[numInterAreaPortals];
|
||||
numInterAreaPortals++;
|
||||
if ( side->planenum == p->onnode->planenum ) {
|
||||
@@ -905,6 +1171,8 @@ static void FindInterAreaPortals_r( node_t *node ) {
|
||||
iap->area1 = p->nodes[0]->area;
|
||||
}
|
||||
iap->side = side;
|
||||
iap->winding = NULL;
|
||||
iap->generated = false;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -934,13 +1202,20 @@ void FloodAreas( uEntity_t *e ) {
|
||||
common->Printf ("%5i areas\n", c_areas);
|
||||
e->numAreas = c_areas;
|
||||
|
||||
MarkOriginalAreas_r( e->tree->headnode );
|
||||
AutoRefineAreas( e );
|
||||
|
||||
// make sure we got all of them
|
||||
CheckAreas_r( e->tree->headnode );
|
||||
|
||||
// identify all portals between areas if this is the world
|
||||
if ( e == &dmapGlobals.uEntities[0] ) {
|
||||
numInterAreaPortals = 0;
|
||||
ClearInterAreaPortals();
|
||||
FindInterAreaPortals_r( e->tree->headnode );
|
||||
if ( dmapGlobals.autoAreaRefine ) {
|
||||
FindGeneratedInterAreaPortals_r( e->tree->headnode );
|
||||
common->Printf( "%5i generated inter-area portals\n", c_autoAreaRefinePortals );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user