diff --git a/doomsday/client/include/world/bsp/hplane.h b/doomsday/client/include/world/bsp/hplane.h index bc8ecad62e..d98781f695 100644 --- a/doomsday/client/include/world/bsp/hplane.h +++ b/doomsday/client/include/world/bsp/hplane.h @@ -28,8 +28,6 @@ #include -#include // M_InverseAngle - #include #include "world/bsp/linesegment.h" @@ -59,19 +57,18 @@ class HPlane */ class Intercept { - public: /// @todo make private: - // True if this intersection was on a self-referencing line. - bool selfRef; - - /// Sector on each side of the vertex (along the partition), or @c 0 - /// if that direction is "closed" (i.e., the intercept point is along - /// 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, + /** + * Construct a new intercept. + * + * @param distance Distance from the origin of the partition line. + * @param lineSeg The intercepted line segment. + * @param edge Relative edge of the line segment nearest to the + * interception point. + * @param meetAtVertex @c true= consider the interception incident with + * nearest edge. + */ + Intercept(double distance, LineSegmentSide &lineSeg, int edge, bool meetAtVertex); bool operator < (Intercept const &other) const { @@ -93,7 +90,12 @@ class HPlane /** * Returns the intercepted line segment. */ - LineSegment::Side &lineSegment() const; + LineSegmentSide &lineSegment() const; + + inline bool lineSegmentIsSelfReferencing() const { + LineSegmentSide &seg = lineSegment(); + return seg.hasMapLine() && seg.mapLine().isSelfReferencing(); + } /** * Returns the identifier for the relevant edge of the intercepted @@ -110,16 +112,33 @@ class HPlane return lineSegment().vertex(lineSegmentEdge()); } + Sector *before() const; + Sector *after() const; + + LineSegmentSide *beforeLineSegment() const; + LineSegmentSide *afterLineSegment() const; + + bool meetAtVertex() const; + #ifdef DENG_DEBUG void debugPrint() const; #endif + friend class HPlane; + private: + /// Sector on each side of the vertex (along the partition), or @c 0 + /// if that direction is "closed" (i.e., the intercept point is along + /// a map line that has no Sector on the relevant side). + LineSegmentSide *_before; + LineSegmentSide *_after; + bool _meetAtVertex; + /// Distance along the half-plane relative to the origin. double _distance; // The intercepted line segment and edge identifier. - LineSegment::Side *_lineSeg; + LineSegmentSide *_lineSeg; int _edge; }; @@ -136,7 +155,7 @@ class HPlane * * @param newLineSeg The "new" line segment to configure using. */ - void configure(LineSegment::Side const &newLineSeg); + void configure(LineSegmentSide const &newLineSeg); /** * Perform intersection of the half-plane with the specified @a lineSeg @@ -150,7 +169,7 @@ class HPlane * @return Distance to intersection point along the half-plane (relative * to the origin). */ - double intersect(LineSegment::Side const &lineSeg, int edge); + double intersect(LineSegmentSide const &lineSeg, int edge); /** * Perform intersection of the half-plane with the specified @a lineSeg. @@ -171,7 +190,7 @@ class HPlane * * @return The resultant new intercept; otherwise @a 0. */ - Intercept *intercept(LineSegment::Side const &lineSeg, int edge, + Intercept *intercept(LineSegmentSide const &lineSeg, int edge, bool meetAtVertex, EdgeTips const &edgeTips); /** @@ -222,7 +241,7 @@ class HPlane * * @see angle() */ - inline coord_t inverseAngle() const { return M_InverseAngle(angle()); } + coord_t inverseAngle() const; /** * Returns the logical @em slopetype for the partition line (which, is determined @@ -238,7 +257,7 @@ class HPlane * chosen as the half-plane partition. May return @c 0 (if no map line was * attributed). */ - LineSegment::Side *lineSegment() const; + LineSegmentSide *lineSegment() const; /** * Calculate @em perpendicular distances from one or both of the vertexe(s) @@ -254,7 +273,7 @@ class HPlane * @param fromDist Perpendicular distance from the "from" vertex. Can be @c 0. * @param toDist Perpendicular distance from the "to" vertex. Can be @c 0. */ - void distance(LineSegment::Side const &lineSegment, coord_t *fromDist = 0, + void distance(LineSegmentSide const &lineSegment, coord_t *fromDist = 0, coord_t *toDist = 0) const; /** @@ -270,7 +289,7 @@ class HPlane * * @return LineRelationship between the partition line and the line segment. */ - LineRelationship relationship(LineSegment::Side const &lineSegment, + LineRelationship relationship(LineSegmentSide const &lineSegment, coord_t *retFromDist, coord_t *retToDist) const; /** @@ -283,10 +302,17 @@ class HPlane */ Intercepts const &intercepts() const; + /** + * Returns the current number of half-plane intercepts. + */ + inline int interceptCount() const { return intercepts().count(); } + private: DENG2_PRIVATE(d) }; +typedef HPlane::Intercept HPlaneIntercept; + } // namespace bsp } // namespace de diff --git a/doomsday/client/include/world/bsp/linesegment.h b/doomsday/client/include/world/bsp/linesegment.h index 39e51c68e7..7d9c30fd1f 100644 --- a/doomsday/client/include/world/bsp/linesegment.h +++ b/doomsday/client/include/world/bsp/linesegment.h @@ -163,6 +163,9 @@ class LineSegment */ bool hasMapSide() const; + /// @copydoc hasMapSide() + inline bool hasMapLine() { return hasMapSide(); } + /** * Returns the map LineSide attributed to "this" side of the line segment. * @@ -559,6 +562,8 @@ class LineSegment DENG2_PRIVATE(d) }; +typedef LineSegment::Side LineSegmentSide; + } // namespace bsp } // namespace de diff --git a/doomsday/client/src/world/bsp/hplane.cpp b/doomsday/client/src/world/bsp/hplane.cpp index e1f25151b1..f5a92f3e59 100644 --- a/doomsday/client/src/world/bsp/hplane.cpp +++ b/doomsday/client/src/world/bsp/hplane.cpp @@ -1,4 +1,4 @@ -/** @file world/bsp/hplane.cpp BSP Builder Half-plane. +/** @file hplane.cpp World map BSP builder half-plane. * * Originally based on glBSP 2.24 (in turn, based on BSP 2.3) * @see http://sourceforge.net/projects/glbsp/ @@ -25,7 +25,8 @@ #include -#include /// @todo remove me +#include // remove me +#include // M_InverseAngle #include @@ -45,18 +46,17 @@ namespace de { namespace bsp { -HPlane::Intercept::Intercept(ddouble distance, LineSegment::Side &lineSeg, int edge, +HPlane::Intercept::Intercept(ddouble distance, LineSegmentSide &lineSeg, int edge, bool meetAtVertex) - : selfRef(false), - before(0), - after(0), - meetAtVertex(meetAtVertex), + : _before(0), + _after(0), + _meetAtVertex(meetAtVertex), _distance(distance), _lineSeg(&lineSeg), _edge(edge) {} -LineSegment::Side &HPlane::Intercept::lineSegment() const +LineSegmentSide &HPlane::Intercept::lineSegment() const { return *_lineSeg; } @@ -66,50 +66,60 @@ int HPlane::Intercept::lineSegmentEdge() const return _edge; } +Sector *HPlane::Intercept::before() const +{ + return _before? _before->sectorPtr() : 0; +} + +Sector *HPlane::Intercept::after() const +{ + return _after? _after->sectorPtr() : 0; +} + +LineSegmentSide *HPlane::Intercept::beforeLineSegment() const +{ + return _before; +} + +LineSegmentSide *HPlane::Intercept::afterLineSegment() const +{ + return _after; +} + +bool HPlane::Intercept::meetAtVertex() const +{ + return _meetAtVertex; +} + #ifdef DENG_DEBUG void HPlane::Intercept::debugPrint() const { LOG_INFO("Vertex #%i %s beforeSector: #%d afterSector: #%d %s") << vertex().indexInMap() << vertex().origin().asText() - << (before? before->indexInArchive() : -1) - << (after? after->indexInArchive() : -1) - << (selfRef? "SELFREF" : ""); + << (_before && _before->hasSector()? _before->sector().indexInArchive() : -1) + << (_after && _after->hasSector()? _after->sector().indexInArchive() : -1); } #endif DENG2_PIMPL(HPlane) { - /// The partition line. - Partition partition; + Partition partition; ///< The partition line. + coord_t length; ///< Direction vector length. + coord_t angle; ///< Cartesian world angle. + slopetype_t slopeType; ///< Logical world angle classification. - /// Direction vector length. - coord_t length; + coord_t perp; ///< Perpendicular scale factor. + coord_t para; ///< Parallel scale factor. - /// World angle. - coord_t angle; + LineSegmentSide *lineSegment; ///< Source of the partition (if any, not owned). - /// Logical line slope (i.e., world angle) classification. - slopetype_t slopeType; + Intercepts intercepts; ///< Points along the half-plane. + bool needSortIntercepts; ///< @c true= @var intercepts requires sorting. - /// Perpendicular scale factor. - coord_t perp; - - /// Parallel scale factor. - coord_t para; - - /// Line segment from which the partition line was derived (if any). - LineSegment::Side *lineSegment; - - /// Intercept points along the half-plane. - Intercepts intercepts; - - /// Set to @c true when @var intercepts requires sorting. - bool needSortIntercepts; - - Instance(Public *i, Partition const &partition_) + Instance(Public *i, Partition const &partition) : Base(i), - partition(partition_), + partition(partition), length(partition.direction.length()), angle(M_DirectionToAngleXY(partition.direction.x, partition.direction.y)), slopeType(M_SlopeTypeXY(partition.direction.x, partition.direction.y)), @@ -120,26 +130,58 @@ DENG2_PIMPL(HPlane) {} /** - * Search the list of intercepts for to see if there is one for the - * specified @a vertex. - * - * @param vertex The vertex to look for. - * - * @return @c true iff an intercept for @a vertex was found. + * Find an intercept by @a vertex. */ - bool haveInterceptForVertex(Vertex const &vertex) const + Intercept *interceptByVertex(Vertex const &vertex) { foreach(Intercept const &icpt, intercepts) { if(&icpt.vertex() == &vertex) - return true; + return const_cast(&icpt); } - return false; + return 0; + } + + /** + * Merges @a next into @a cur. + */ + static void mergeIntercepts(HPlane::Intercept &cur, HPlane::Intercept const &next) + { + /* + LOG_AS("HPlane::mergeIntercepts"); + cur.debugPrint(); + next.debugPrint(); + */ + + if(&cur.lineSegment().line() == &next.lineSegment().line()) + return; + + bool curSelfRef = (cur.lineSegment().hasMapSide() && cur.lineSegment().mapLine().isSelfReferencing()); + bool nextSelfRef = (next.lineSegment().hasMapSide() && next.lineSegment().mapLine().isSelfReferencing()); + + if(curSelfRef && !nextSelfRef) + { + if(cur.before() && next.before()) + cur._before = next._before; + + if(cur.after() && next.after()) + cur._after = next._after; + } + + if(!cur.before() && next.before()) + cur._before = next._before; + + if(!cur.after() && next.after()) + cur._after = next._after; + + /* + LOG_TRACE("Result:"); + cur.debugPrint(); + */ } }; -HPlane::HPlane(Partition const &partition) - : d(new Instance(this, partition)) +HPlane::HPlane(Partition const &partition) : d(new Instance(this, partition)) {} void HPlane::clearIntercepts() @@ -149,7 +191,7 @@ void HPlane::clearIntercepts() d->needSortIntercepts = false; } -void HPlane::configure(LineSegment::Side const &newBaseSeg) +void HPlane::configure(LineSegmentSide const &newBaseSeg) { // Only map line segments are suitable. DENG_ASSERT(newBaseSeg.hasMapSide()); @@ -165,7 +207,7 @@ void HPlane::configure(LineSegment::Side const &newBaseSeg) d->partition.direction = mapSide.to().origin() - mapSide.from().origin(); d->partition.origin = mapSide.from().origin(); - d->lineSegment = const_cast(&newBaseSeg); + d->lineSegment = const_cast(&newBaseSeg); d->length = d->partition.direction.length(); d->angle = M_DirectionToAngleXY(d->partition.direction.x, d->partition.direction.y); @@ -189,7 +231,7 @@ void HPlane::configure(LineSegment::Side const &newBaseSeg) * * @return The "open" sector at this angle; otherwise @c 0 (closed). */ -static Sector *openSectorAtAngle(EdgeTips const &tips, coord_t angle) +static LineSegmentSide *lineSegAtAngle(EdgeTips const &tips, coord_t angle) { DENG_ASSERT(!tips.isEmpty()); @@ -213,18 +255,17 @@ static Sector *openSectorAtAngle(EdgeTips const &tips, coord_t angle) EdgeTip const &tip = *it; if(angle + ANG_EPSILON < tip.angle()) { - // Found it. - return (tip.hasFront()? tip.front().sectorPtr() : 0); + return tip.hasFront()? &tip.front() : 0; } } // Not found. The open sector will therefore be on the back of the tip // at the greatest angle. EdgeTip const &tip = tips.all().back(); - return (tip.hasBack()? tip.back().sectorPtr() : 0); + return tip.hasBack()? &tip.back() : 0; } -double HPlane::intersect(LineSegment::Side const &lineSeg, int edge) +double HPlane::intersect(LineSegmentSide const &lineSeg, int edge) { Vertex &vertex = lineSeg.vertex(edge); coord_t pointV1[2] = { vertex.origin().x, vertex.origin().y }; @@ -232,64 +273,38 @@ double HPlane::intersect(LineSegment::Side const &lineSeg, int edge) return V2d_PointLineParaDistance(pointV1, directionV1, d->para, d->length); } -HPlane::Intercept *HPlane::intercept(LineSegment::Side const &lineSeg, int edge, +HPlane::Intercept *HPlane::intercept(LineSegmentSide const &lineSeg, int edge, 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(lineSeg), edge, meetAtVertex)); - Intercept *newIntercept = &d->intercepts.last(); + bool selfRef = (lineSeg.hasMapSide() && lineSeg.mapLine().isSelfReferencing()); - newIntercept->selfRef = (lineSeg.hasMapSide() && lineSeg.mapLine().isSelfReferencing()); - - newIntercept->before = openSectorAtAngle(edgeTips, inverseAngle()); - newIntercept->after = openSectorAtAngle(edgeTips, angle()); - - // The addition of a new intercept means we'll need to resort. - d->needSortIntercepts = true; - - return newIntercept; -} - -/** - * Merges @a next into @a cur. - */ -static void mergeIntercepts(HPlane::Intercept &cur, HPlane::Intercept const &next) -{ - /* - LOG_AS("HPlane::mergeIntercepts"); - cur.debugPrint(); - next.debugPrint(); - */ - - if(&cur.lineSegment().line() == &next.lineSegment().line()) - return; - - if(cur.selfRef && !next.selfRef) + // Already present for this vertex? + Intercept *icpt; + if((icpt = d->interceptByVertex(lineSeg.vertex(edge)))) { - if(cur.before && next.before) - cur.before = next.before; - - if(cur.after && next.after) - cur.after = next.after; - - cur.selfRef = false; + // If the new intercept line is not self-referencing we'll replace it. + if(!(icpt->lineSegmentIsSelfReferencing() && !selfRef)) + { + return icpt; + } } + else + { + d->intercepts.append( + Intercept(intersect(lineSeg, edge), const_cast(lineSeg), + edge, meetAtVertex)); + icpt = &d->intercepts.last(); - if(!cur.before && next.before) - cur.before = next.before; + // The addition of a new intercept means we'll need to resort. + d->needSortIntercepts = true; + } - if(!cur.after && next.after) - cur.after = next.after; + icpt->_lineSeg = const_cast(&lineSeg); + icpt->_edge = edge; + icpt->_before = lineSegAtAngle(edgeTips, inverseAngle()); + icpt->_after = lineSegAtAngle(edgeTips, angle()); - /* - LOG_TRACE("Result:"); - cur.debugPrint(); - */ + return icpt; } void HPlane::sortAndMergeIntercepts() @@ -318,7 +333,7 @@ void HPlane::sortAndMergeIntercepts() if(distance <= HPLANE_INTERCEPT_MERGE_DISTANCE_EPSILON) { // Yes - merge the "next" intercept into "cur". - mergeIntercepts(cur, next); + d->mergeIntercepts(cur, next); // Destroy the "next" intercept. d->intercepts.removeAt(i+1); @@ -341,17 +356,22 @@ coord_t HPlane::angle() const return d->angle; } +coord_t HPlane::inverseAngle() const +{ + return M_InverseAngle(angle()); +} + slopetype_t HPlane::slopeType() const { return d->slopeType; } -LineSegment::Side *HPlane::lineSegment() const +LineSegmentSide *HPlane::lineSegment() const { return d->lineSegment; } -void HPlane::distance(LineSegment::Side const &lineSeg, coord_t *fromDist, coord_t *toDist) const +void HPlane::distance(LineSegmentSide const &lineSeg, coord_t *fromDist, coord_t *toDist) const { // Any work to do? if(!fromDist && !toDist) return; @@ -382,7 +402,7 @@ void HPlane::distance(LineSegment::Side const &lineSeg, coord_t *fromDist, coord } } -LineRelationship HPlane::relationship(LineSegment::Side const &lineSeg, +LineRelationship HPlane::relationship(LineSegmentSide const &lineSeg, coord_t *retFromDist, coord_t *retToDist) const { coord_t fromDist, toDist; diff --git a/doomsday/client/src/world/bsp/partitioner.cpp b/doomsday/client/src/world/bsp/partitioner.cpp index 04457d1c48..9cb53beb75 100644 --- a/doomsday/client/src/world/bsp/partitioner.cpp +++ b/doomsday/client/src/world/bsp/partitioner.cpp @@ -715,7 +715,7 @@ DENG2_PIMPL(Partitioner) return point; } - /// @todo refactor away -ds + /// @todo refactor away inline void interceptPartition(LineSegment::Side &seg, int edge, bool meetAtVertex = false) { hplane.intercept(seg, edge, meetAtVertex, edgeTips(seg.vertex(edge))); @@ -910,8 +910,7 @@ DENG2_PIMPL(Partitioner) } // Create new line segments. - Sector *prevSector = 0; - for(int i = 0; i < hplane.intercepts().count() - 1; ++i) + for(int i = 0; i < hplane.interceptCount() - 1; ++i) { HPlane::Intercept const &cur = hplane.intercepts()[i]; HPlane::Intercept const &next = hplane.intercepts()[i+1]; @@ -920,32 +919,27 @@ DENG2_PIMPL(Partitioner) if(partSeg && cur.distance() >= nearDist && next.distance() <= farDist) continue; - if(!cur.after && !next.before) - { - prevSector = 0; + if(!cur.after() && !next.before()) continue; - } // Check for some nasty open/closed or close/open cases. - if(cur.after && !next.before) + if(cur.after() && !next.before()) { - if(!cur.selfRef) + if(!cur.lineSegmentIsSelfReferencing()) { Vector2d nearPoint = (cur.vertex().origin() + next.vertex().origin()) / 2; - notifyUnclosedSectorFound(*cur.after, nearPoint); + notifyUnclosedSectorFound(*cur.after(), nearPoint); } - prevSector = 0; continue; } - if(!cur.after && next.before) + if(!cur.after() && next.before()) { - if(!next.selfRef) + if(!next.lineSegmentIsSelfReferencing()) { Vector2d nearPoint = (cur.vertex().origin() + next.vertex().origin()) / 2; - notifyUnclosedSectorFound(*next.before, nearPoint); + notifyUnclosedSectorFound(*next.before(), nearPoint); } - prevSector = 0; continue; } @@ -955,35 +949,35 @@ DENG2_PIMPL(Partitioner) Vertex &fromVertex = cur.vertex(); Vertex &toVertex = next.vertex(); - Sector *sector = cur.after; - if(prevSector && cur.meetAtVertex && cur.before == cur.after) + Sector *sector = cur.after(); + if(!cur.before() && next.before() == next.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; + sector = next.before(); } else { // Choose the non-self-referencing sector when we can. - if(cur.after != next.before) + if(cur.after() != next.before()) { - if(!cur.selfRef && !next.selfRef) + if(!cur.lineSegmentIsSelfReferencing() && + !next.lineSegmentIsSelfReferencing()) { LOG_DEBUG("Sector mismatch #%d %s != #%d %s.") - << cur.after->indexInMap() + << cur.after()->indexInMap() << cur.vertex().origin().asText() - << next.before->indexInMap() + << next.before()->indexInMap() << next.vertex().origin().asText(); } - if(cur.selfRef && !next.selfRef) - sector = next.before; + LineSegmentSide *afterSeg = cur.afterLineSegment(); + if(afterSeg->hasMapLine() && afterSeg->mapLine().isSelfReferencing()) + { + LineSegmentSide *beforeSeg = next.beforeLineSegment(); + if(beforeSeg->hasMapLine() && !beforeSeg->mapLine().isSelfReferencing()) + { + sector = next.before(); + } + } } } @@ -1007,8 +1001,6 @@ DENG2_PIMPL(Partitioner) << toVertex.origin().asText() << sector->indexInArchive(); */ - - prevSector = sector; } }