From f23cccb18eaf38209674ade0f4c8357daba22a38 Mon Sep 17 00:00:00 2001 From: danij Date: Sat, 7 Apr 2012 00:40:26 +0100 Subject: [PATCH] BSP Builder|Refactor: Re-implemented all recursive SuperBlockmap traversals as iterative Switching to iterative algorithms not only alleviates stack concerns and speeds up the node build greatly but it also solves all of the object API issues. Win win. --- .../portable/include/map/bsp/linedefinfo.h | 5 +- .../portable/include/map/bsp/partitioner.h | 5 +- .../portable/include/map/bsp/superblockmap.h | 3 +- .../portable/src/map/bsp/partitioner.cpp | 260 +++++++++++------- .../portable/src/map/bsp/superblockmap.cpp | 79 ++++-- 5 files changed, 227 insertions(+), 125 deletions(-) diff --git a/doomsday/engine/portable/include/map/bsp/linedefinfo.h b/doomsday/engine/portable/include/map/bsp/linedefinfo.h index 6f3d8d2dfd..e6c2deab57 100644 --- a/doomsday/engine/portable/include/map/bsp/linedefinfo.h +++ b/doomsday/engine/portable/include/map/bsp/linedefinfo.h @@ -56,7 +56,10 @@ struct LineDefInfo /// No hedges should be created for these overlapping linedefs. LineDef* overlap; - LineDefInfo::LineDefInfo() : flags(0), overlap(0) + /// @todo Refactor me away. + int validCount; + + LineDefInfo::LineDefInfo() : flags(0), overlap(0), validCount(0) {} }; diff --git a/doomsday/engine/portable/include/map/bsp/partitioner.h b/doomsday/engine/portable/include/map/bsp/partitioner.h index 2bdbb3c510..cad80372c2 100644 --- a/doomsday/engine/portable/include/map/bsp/partitioner.h +++ b/doomsday/engine/portable/include/map/bsp/partitioner.h @@ -250,14 +250,15 @@ class Partitioner * also reworked, heavily). I think it is important that both these routines * follow the exact same logic. */ -public: void divideHEdge(HEdge* hedge, SuperBlock& rightList, SuperBlock& leftList); -private: void clearPartitionIntercepts(); bool configurePartition(const HEdge* hedge); + void chooseHEdgeFromSuperBlock(SuperBlock* partList, SuperBlock& hedgeList, + HEdge** best, int* bestCost); + /** * Find the best half-edge in the list to use as the next partition. * diff --git a/doomsday/engine/portable/include/map/bsp/superblockmap.h b/doomsday/engine/portable/include/map/bsp/superblockmap.h index 6320d344e9..7c5efece43 100644 --- a/doomsday/engine/portable/include/map/bsp/superblockmap.h +++ b/doomsday/engine/portable/include/map/bsp/superblockmap.h @@ -245,7 +245,8 @@ class SuperBlock friend class SuperBlockmap; }; -class SuperBlockmap { +class SuperBlockmap +{ public: /** * @param bounds Bounding box in map coordinates for the whole blockmap. diff --git a/doomsday/engine/portable/src/map/bsp/partitioner.cpp b/doomsday/engine/portable/src/map/bsp/partitioner.cpp index 33ea2d2930..042c808acf 100644 --- a/doomsday/engine/portable/src/map/bsp/partitioner.cpp +++ b/doomsday/engine/portable/src/map/bsp/partitioner.cpp @@ -35,8 +35,11 @@ #include "de_base.h" #include "de_console.h" -#include "de_play.h" +#include "bspleaf.h" +#include "bspnode.h" +#include "hedge.h" #include "p_mapdata.h" +#include "p_maputil.h" #include "m_misc.h" #include "map/bsp/hedgeintercept.h" @@ -669,52 +672,6 @@ static int evalPartition(SuperBlock& block, int splitCostFactor, return cost.total; } -typedef struct { - SuperBlock* hedgeList; - int splitCostFactor; - HEdge* best; - int bestCost; -} choosehedgefromsuperblockparams_t; - -static int chooseHEdgeFromSuperBlock(SuperBlock* partList, void* parameters) -{ - choosehedgefromsuperblockparams_t* p = (choosehedgefromsuperblockparams_t*)parameters; - - // Test each half-edge as a potential partition. - for(SuperBlock::HEdges::const_iterator it = partList->hedgesBegin(); - it != partList->hedgesEnd(); ++it) - { - HEdge* hedge = *it; - - //LOG_DEBUG("chooseHEdgeFromSuperBlock: %shedge %p sector:%d [%1.1f, %1.1f] -> [%1.1f, %1.1f]") - // << (lineDef? "" : "mini-") << hedge - // << (hedge->bspBuildInfo->sector? hedge->bspBuildInfo->sector->index : -1) - // << hedge->v[0]->V_pos[VX] << hedge->v[0]->V_pos[VY] - // << hedge->v[1]->V_pos[VX] << hedge->v[1]->V_pos[VY]; - - // "Mini-hedges" are never potential candidates. - LineDef* lineDef = hedge->bspBuildInfo->lineDef; - if(!lineDef) continue; - - // Only test half-edges from the same linedef once per round of - // partition picking (they are collinear). - if(lineDef->validCount == validCount) continue; - lineDef->validCount = validCount; - - // Unsuitable or too costly? - int cost = evalPartition(*p->hedgeList, p->splitCostFactor, hedge->bspBuildInfo, p->bestCost); - if(cost >= 0 && cost < p->bestCost) - { - // We have a new better choice. - p->bestCost = cost; - - // Remember which half-edge. - p->best = hedge; - } - } - return false; // Continue iteration. -} - void Partitioner::clearPartitionIntercepts() { for(HPlane::Intercepts::iterator it = partition->begin(); it != partition->end(); ++it) @@ -753,29 +710,98 @@ bool Partitioner::configurePartition(const HEdge* hedge) return true; } +void Partitioner::chooseHEdgeFromSuperBlock(SuperBlock* partList, SuperBlock& hedgeList, + HEdge** best, int* bestCost) +{ + // Test each half-edge as a potential partition. + for(SuperBlock::HEdges::const_iterator it = partList->hedgesBegin(); + it != partList->hedgesEnd(); ++it) + { + HEdge* hedge = *it; + + //LOG_DEBUG("chooseHEdgeFromSuperBlock: %shedge %p sector:%d [%1.1f, %1.1f] -> [%1.1f, %1.1f]") + // << (lineDef? "" : "mini-") << hedge + // << (hedge->bspBuildInfo->sector? hedge->bspBuildInfo->sector->index : -1) + // << hedge->v[0]->V_pos[VX] << hedge->v[0]->V_pos[VY] + // << hedge->v[1]->V_pos[VX] << hedge->v[1]->V_pos[VY]; + + // "Mini-hedges" are never potential candidates. + LineDef* lineDef = hedge->bspBuildInfo->lineDef; + if(!lineDef) continue; + + // Only test half-edges from the same linedef once per round of + // partition picking (they are collinear). + LineDefInfo& lInfo = lineDefInfo(*lineDef); + if(lInfo.validCount == validCount) continue; + lInfo.validCount = validCount; + + // Unsuitable or too costly? + int cost = evalPartition(hedgeList, splitCostFactor, hedge->bspBuildInfo, *bestCost); + if(cost >= 0 && cost < *bestCost) + { + // We have a new better choice. + *bestCost = cost; + + // Remember which half-edge. + *best = hedge; + } + } +} + bool Partitioner::chooseNextPartition(SuperBlock& hedgeList) { - choosehedgefromsuperblockparams_t parm; - parm.hedgeList = &hedgeList; - parm.splitCostFactor = splitCostFactor; - parm.best = NULL; - parm.bestCost = DDMAXINT; + HEdge* best = NULL; + int bestCost = DDMAXINT; + // Increment valid count so we can avoid testing the half edges produced + // from a single linedef more than once per round of partition selection. validCount++; - if(hedgeList.traverse(chooseHEdgeFromSuperBlock, (void*)&parm)) + + // Iterative pre-order traversal of SuperBlock. + SuperBlock* cur = &hedgeList; + SuperBlock* prev = 0; + while(cur) { - /// @kludge Partitioner::buildNodes() will detect the cancellation. - return false; + while(cur) + { + chooseHEdgeFromSuperBlock(cur, hedgeList, &best, &bestCost); + + if(prev == cur->parent()) + { + // Descending - right first, then left. + prev = cur; + if(cur->hasRight()) cur = cur->right(); + else cur = cur->left(); + } + else if(prev == cur->right()) + { + // Last moved up the right branch - descend the left. + prev = cur; + cur = cur->left(); + } + else if(prev == cur->left()) + { + // Last moved up the left branch - continue upward. + prev = cur; + cur = cur->parent(); + } + } + + if(prev) + { + // No right child - back up. + cur = prev->parent(); + } } - /*if(parm.best) + /*if(best) { LOG_DEBUG("Partitioner::choosePartition: best %p score: %d.%02d.") << best << bestCost / 100 << bestCost % 100; }*/ // Reconfigure the half plane for the next round of hedge sorting. - return configurePartition(parm.best); + return configurePartition(best); } const HPlaneIntercept* Partitioner::makePartitionIntersection(HEdge* hedge, int leftSide) @@ -940,34 +966,48 @@ void Partitioner::divideHEdge(HEdge* hedge, SuperBlock& rightList, SuperBlock& l #undef RIGHT } -typedef struct { - SuperBlock* rights; - SuperBlock* lefts; - Partitioner* partitioner; -} partitionhedgeworkerparams_t; - -int C_DECL Partitioner_PartitionHEdgeWorker(SuperBlock* superblock, void* parameters) +void Partitioner::partitionHEdges(SuperBlock& hedgeList, SuperBlock& rights, SuperBlock& lefts) { - partitionhedgeworkerparams_t* p = (partitionhedgeworkerparams_t*)parameters; - HEdge* hedge; - assert(p); - - while((hedge = superblock->pop())) + // Iterative pre-order traversal of SuperBlock. + SuperBlock* cur = &hedgeList; + SuperBlock* prev = 0; + while(cur) { - p->partitioner->divideHEdge(hedge, *p->rights, *p->lefts); - } - - return false; // Continue iteration. -} + while(cur) + { + HEdge* hedge; + while((hedge = cur->pop())) + { + divideHEdge(hedge, rights, lefts); + } -void Partitioner::partitionHEdges(SuperBlock& hedgeList, SuperBlock& rights, SuperBlock& lefts) -{ - partitionhedgeworkerparams_t parm; + if(prev == cur->parent()) + { + // Descending - right first, then left. + prev = cur; + if(cur->hasRight()) cur = cur->right(); + else cur = cur->left(); + } + else if(prev == cur->right()) + { + // Last moved up the right branch - descend the left. + prev = cur; + cur = cur->left(); + } + else if(prev == cur->left()) + { + // Last moved up the left branch - continue upward. + prev = cur; + cur = cur->parent(); + } + } - parm.rights = &rights; - parm.lefts = &lefts; - parm.partitioner = this; - hedgeList.traverse(Partitioner_PartitionHEdgeWorker, (void*)&parm); + if(prev) + { + // No right child - back up. + cur = prev->parent(); + } + } // Sanity checks... if(!rights.totalHEdgeCount()) @@ -977,27 +1017,53 @@ void Partitioner::partitionHEdges(SuperBlock& hedgeList, SuperBlock& rights, Sup Con_Error("Partitioner::partitionhedges: Separated half-edge has no left side."); } -static int createBSPLeafWorker(SuperBlock* superblock, void* parameters) +BspLeaf* Partitioner::createBSPLeaf(SuperBlock& hedgeList) { - BspLeaf* leaf = (BspLeaf*) parameters; - HEdge* hedge; - assert(leaf); + BspLeaf* leaf = BspLeaf_New(); - while((hedge = superblock->pop())) + // Iterative pre-order traversal of SuperBlock. + SuperBlock* cur = &hedgeList; + SuperBlock* prev = 0; + while(cur) { - // Link it into head of the leaf's list. - hedge->next = leaf->hedge; - leaf->hedge = hedge; - } + while(cur) + { + HEdge* hedge; + while((hedge = cur->pop())) + { + // Link it into head of the leaf's list. + hedge->next = leaf->hedge; + leaf->hedge = hedge; + } - return false; // Continue iteration. -} + if(prev == cur->parent()) + { + // Descending - right first, then left. + prev = cur; + if(cur->hasRight()) cur = cur->right(); + else cur = cur->left(); + } + else if(prev == cur->right()) + { + // Last moved up the right branch - descend the left. + prev = cur; + cur = cur->left(); + } + else if(prev == cur->left()) + { + // Last moved up the left branch - continue upward. + prev = cur; + cur = cur->parent(); + } + } + + if(prev) + { + // No right child - back up. + cur = prev->parent(); + } + } -BspLeaf* Partitioner::createBSPLeaf(SuperBlock& hedgeList) -{ - BspLeaf* leaf = BspLeaf_New(); - // Link the half-edges into the new leaf. - hedgeList.traverse(createBSPLeafWorker, leaf); return leaf; } diff --git a/doomsday/engine/portable/src/map/bsp/superblockmap.cpp b/doomsday/engine/portable/src/map/bsp/superblockmap.cpp index e3b73aea42..0baab42459 100644 --- a/doomsday/engine/portable/src/map/bsp/superblockmap.cpp +++ b/doomsday/engine/portable/src/map/bsp/superblockmap.cpp @@ -352,45 +352,76 @@ SuperBlock& SuperBlockmap::root() return *static_cast(KdTreeNode_UserData(KdTree_Root(d->kdTree))); } -typedef struct { - AABoxf bounds; - boolean initialized; -} findhedgelistboundsparams_t; +void SuperBlockmap::clear() +{ + d->clearBlockWorker(root()); +} -static int findHEdgeBoundsWorker(SuperBlock* block, void* parameters) +static void findHEdgeBoundsWorker(SuperBlock& block, AABoxf& bounds, bool* initialized) { - findhedgelistboundsparams_t* p = (findhedgelistboundsparams_t*)parameters; - if(block->hedgeCount(true, true)) + Q_ASSERT(initialized); + if(block.hedgeCount(true, true)) { AABoxf blockHEdgeAABox; - block->findHEdgeBounds(blockHEdgeAABox); - if(p->initialized) + block.findHEdgeBounds(blockHEdgeAABox); + if(*initialized) { - V2f_AddToBox(p->bounds.arvec2, blockHEdgeAABox.min); + V2f_AddToBox(bounds.arvec2, blockHEdgeAABox.min); } else { - V2f_InitBox(p->bounds.arvec2, blockHEdgeAABox.min); - p->initialized = true; + V2f_InitBox(bounds.arvec2, blockHEdgeAABox.min); + *initialized = true; } - V2f_AddToBox(p->bounds.arvec2, blockHEdgeAABox.max); + V2f_AddToBox(bounds.arvec2, blockHEdgeAABox.max); } - return false; // Continue iteration. -} - -void SuperBlockmap::clear() -{ - d->clearBlockWorker(root()); } void SuperBlockmap::findHEdgeBounds(AABoxf& aaBox) { - findhedgelistboundsparams_t parm; - parm.initialized = false; - root().traverse(findHEdgeBoundsWorker, (void*)&parm); - if(parm.initialized) + bool initialized = false; + AABoxf bounds; + + // Iterative pre-order traversal of SuperBlock. + SuperBlock* cur = &root(); + SuperBlock* prev = 0; + while(cur) + { + while(cur) + { + findHEdgeBoundsWorker(*cur, bounds, &initialized); + + if(prev == cur->parent()) + { + // Descending - right first, then left. + prev = cur; + if(cur->hasRight()) cur = cur->right(); + else cur = cur->left(); + } + else if(prev == cur->right()) + { + // Last moved up the right branch - descend the left. + prev = cur; + cur = cur->left(); + } + else if(prev == cur->left()) + { + // Last moved up the left branch - continue upward. + prev = cur; + cur = cur->parent(); + } + } + + if(prev) + { + // No right child - back up. + cur = prev->parent(); + } + } + + if(initialized) { - V2f_CopyBox(aaBox.arvec2, parm.bounds.arvec2); + V2f_CopyBox(aaBox.arvec2, bounds.arvec2); return; }