dmap now contains path tracing optimizations stored in iceproc files.

This commit is contained in:
Justin Marshall
2026-05-26 12:14:39 -07:00
parent 754f5b4aa0
commit 41cbd5669b
50 changed files with 350449 additions and 624741 deletions
+276 -1
View File
@@ -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 );
}
}
}