Skip to content

Commit

Permalink
Fixed|BSP Builder: Space partitioner logic error resulting in mismatc…
Browse files Browse the repository at this point in the history
…hed sectors

When building line segments along the partitioning half-plane care
must be taken with the edge case of intercept point incident which
is incident with a vertex. In this situation if both "open" sectors
are the same it means that we are building one or more line segments
between a vertex and an outwardly convex "point". In such a case the
sector of the last non-point should propagate along the partition
line rather than choosing a sector from the current intercept pair.

By my reckoning this addresses the last of the serious issues in the
space partitioner given well-formed input data.

We are now ready to introduce algorithms for linking the planes of
certain BSP leafs to other sectors.

Todo for later: This should be considered a kludge and not a final
solution. The intercept model used here is insufficient to represent
all the cases and should be revised.
  • Loading branch information
danij-deng committed Jun 2, 2013
1 parent cf75e94 commit 550a02d
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 24 deletions.
10 changes: 8 additions & 2 deletions doomsday/client/include/map/bsp/hplane.h
Expand Up @@ -66,9 +66,11 @@ class HPlane
/// a map line that has no Sector on the relevant side).
Sector *before;
Sector *after;
bool meetAtVertex;

public:
Intercept(double distance, LineSegment::Side &lineSeg, int edge);
Intercept(double distance, LineSegment::Side &lineSeg, int edge,
bool meetAtVertex);

bool operator < (Intercept const &other) const {
return _distance < other._distance;
Expand Down Expand Up @@ -158,13 +160,17 @@ class HPlane
* @param lineSeg Line segment to perform intersection with.
* @param edge Line segment edge identifier of the vertex to associate
* with any resulting intercept.
*
* @param meetAtVertex @c true intercept point is close enough to the
* @a edge vertex to be considered incident.
*
* @param edgeTips Set of EdgeTips for the identified @a edge of
* @a lineSeg. (@todo Refactor away -ds)
*
* @return The resultant new intercept; otherwise @a 0.
*/
Intercept *intercept(LineSegment::Side const &lineSeg, int edge,
EdgeTips const &edgeTips);
bool meetAtVertex, EdgeTips const &edgeTips);

/**
* Sort and then merge near-intercepts from the given list.
Expand Down
14 changes: 8 additions & 6 deletions doomsday/client/src/map/bsp/hplane.cpp
Expand Up @@ -43,10 +43,12 @@
namespace de {
namespace bsp {

HPlane::Intercept::Intercept(ddouble distance, LineSegment::Side &lineSeg, int edge)
HPlane::Intercept::Intercept(ddouble distance, LineSegment::Side &lineSeg, int edge,
bool meetAtVertex)
: selfRef(false),
before(0),
after(0),
meetAtVertex(meetAtVertex),
_distance(distance),
_lineSeg(&lineSeg),
_edge(edge)
Expand All @@ -68,8 +70,8 @@ void HPlane::Intercept::debugPrint() const
LOG_INFO("Vertex #%i %s beforeSector: #%d afterSector: #%d %s")
<< vertex().indexInMap()
<< vertex().origin().asText()
<< (before? before->indexInMap() : -1)
<< (after? after->indexInMap() : -1)
<< (before? before->indexInArchive() : -1)
<< (after? after->indexInArchive() : -1)
<< (selfRef? "SELFREF" : "");
}
#endif
Expand Down Expand Up @@ -229,15 +231,15 @@ double HPlane::intersect(LineSegment::Side const &lineSeg, int edge)
}

HPlane::Intercept *HPlane::intercept(LineSegment::Side const &lineSeg, int edge,
EdgeTips const &edgeTips)
bool meetAtVertex, EdgeTips const &edgeTips)
{
// Already present for this vertex?
Vertex &vertex = lineSeg.vertex(edge);
if(d->haveInterceptForVertex(vertex)) return 0;

coord_t distToVertex = intersect(lineSeg, edge);

d->intercepts.append(Intercept(distToVertex, const_cast<LineSegment::Side &>(lineSeg), edge));
d->intercepts.append(Intercept(distToVertex, const_cast<LineSegment::Side &>(lineSeg), edge, meetAtVertex));
Intercept *newIntercept = &d->intercepts.last();

newIntercept->selfRef = (lineSeg.hasMapSide() && lineSeg.mapLine().isSelfReferencing());
Expand Down Expand Up @@ -403,7 +405,7 @@ void HPlane::printIntercepts() const
uint index = 0;
foreach(Intercept const &icpt, d->intercepts)
{
LOG_DEBUG(" %u: >%1.2f ") << (index++) << icpt.distance();
LOG_DEBUG(" %u: >%1.2f") << (index++) << icpt.distance();
icpt.debugPrint();
}
}
Expand Down
52 changes: 36 additions & 16 deletions doomsday/client/src/map/bsp/partitioner.cpp
Expand Up @@ -900,9 +900,9 @@ DENG2_PIMPL(Partitioner)
}

/// @todo refactor away -ds
inline void interceptPartition(LineSegment::Side &seg, int edge)
inline void interceptPartition(LineSegment::Side &seg, int edge, bool meetAtVertex = false)
{
hplane.intercept(seg, edge, edgeTips(seg.vertex(edge)));
hplane.intercept(seg, edge, meetAtVertex, edgeTips(seg.vertex(edge)));
}

/**
Expand Down Expand Up @@ -949,7 +949,8 @@ DENG2_PIMPL(Partitioner)
{
// Direction determines which edge of the line segment interfaces
// with the new half-plane intercept.
interceptPartition(seg, (fromDist < DIST_EPSILON? LineSegment::From : LineSegment::To));
interceptPartition(seg, (fromDist < DIST_EPSILON? LineSegment::From : LineSegment::To),
true /*incident with the edge vertex*/);
}
linkSegmentInSuperBlockmap(rights, seg);
break;
Expand All @@ -958,7 +959,8 @@ DENG2_PIMPL(Partitioner)
case LeftIntercept:
if(rel == LeftIntercept)
{
interceptPartition(seg, (fromDist > -DIST_EPSILON? LineSegment::From : LineSegment::To));
interceptPartition(seg, (fromDist > -DIST_EPSILON? LineSegment::From : LineSegment::To),
true /*incident with the edge vertex*/);
}
linkSegmentInSuperBlockmap(lefts, seg);
break;
Expand Down Expand Up @@ -1093,18 +1095,22 @@ DENG2_PIMPL(Partitioner)
}

// Create new line segments.
Sector *prevSector = 0;
for(int i = 0; i < hplane.intercepts().count() - 1; ++i)
{
HPlane::Intercept const &cur = hplane.intercepts()[i];
HPlane::Intercept const &next = hplane.intercepts()[i+1];

if(!cur.after && !next.before)
continue;

// Does this range overlap the partition line segment?
if(partSeg && cur.distance() >= nearDist && next.distance() <= farDist)
continue;

if(!cur.after && !next.before)
{
prevSector = 0;
continue;
}

// Check for some nasty open/closed or close/open cases.
if(cur.after && !next.before)
{
Expand All @@ -1113,6 +1119,7 @@ DENG2_PIMPL(Partitioner)
Vector2d nearPoint = (cur.vertex().origin() + next.vertex().origin()) / 2;
notifyUnclosedSectorFound(*cur.after, nearPoint);
}
prevSector = 0;
continue;
}

Expand All @@ -1123,15 +1130,26 @@ DENG2_PIMPL(Partitioner)
Vector2d nearPoint = (cur.vertex().origin() + next.vertex().origin()) / 2;
notifyUnclosedSectorFound(*next.before, nearPoint);
}
prevSector = 0;
continue;
}

/*
* This is definitely open space.
*/
Vertex &fromVertex = cur.vertex();
Vertex &toVertex = next.vertex();

Sector *sector = cur.after;
if(!cur.before && next.before == next.after)
if(prevSector && cur.meetAtVertex && cur.before == cur.after)
{
sector = prevSector;
}
else if(prevSector && next.meetAtVertex && next.before == next.after)
{
sector = prevSector;
}
else if(!cur.before && next.before == next.after)
{
sector = next.before;
}
Expand All @@ -1142,7 +1160,7 @@ DENG2_PIMPL(Partitioner)
{
if(!cur.selfRef && !next.selfRef)
{
LOG_DEBUG("Sector mismatch (#%d %s != #%d %s.")
LOG_DEBUG("Sector mismatch #%d %s != #%d %s")
<< cur.after->indexInMap()
<< cur.vertex().origin().asText()
<< next.before->indexInMap()
Expand All @@ -1157,7 +1175,7 @@ DENG2_PIMPL(Partitioner)
DENG_ASSERT(sector != 0);

LineSegment &newSeg =
buildLineSegmentBetweenVertexes(cur.vertex(), next.vertex(),
buildLineSegmentBetweenVertexes(fromVertex, toVertex,
sector, sector, 0 /*no map line*/,
partSeg? &partSeg->mapLine() : 0);

Expand All @@ -1170,10 +1188,12 @@ DENG2_PIMPL(Partitioner)

/*
LOG_DEBUG("Built line segment from %s to %s (sector #%i)")
<< newSeg.from().origin().asText()
<< newSeg.to().origin().asText()
<< fromVertex.origin().asText()
<< toVertex.origin().asText()
<< sector->indexInArchive();
*/

prevSector = sector;
}
}

Expand Down Expand Up @@ -1265,11 +1285,11 @@ DENG2_PIMPL(Partitioner)
// Reconfigure the half-plane for the next round of partitioning.
hplane.configure(*partSeg);

//LOG_TRACE("%s, line segment %p %s %s.")
//LOG_TRACE("%s, line segment [%p] %s %s.")
// << hplane.partition().asText()
// << de::dintptr(*partSeg)
// << partSeg->fromOrigin().asText()
// << partSeg->toOrigin().asText()
// << de::dintptr(partSeg)
// << partSeg->from().origin().asText()
// << partSeg->to().origin().asText();

// Take a copy of the current partition - we'll need this for any
// BspNode we produce later.
Expand Down

0 comments on commit 550a02d

Please sign in to comment.