Skip to content

Commit

Permalink
BSP Builder|ConvexSubspace: Added ConvexSubspace
Browse files Browse the repository at this point in the history
Will assume responsibility for analyzing the partitioned segments
and assigning them to "continuity" groups for geometry construction.
  • Loading branch information
danij-deng committed May 27, 2013
1 parent 9f34606 commit 5b5c3e2
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 0 deletions.
2 changes: 2 additions & 0 deletions doomsday/client/client.pro
Expand Up @@ -231,6 +231,7 @@ DENG_HEADERS += \
include/map/blockmap.h \
include/map/blockmapvisual.h \
include/map/bsp/bsptreenode.h \
include/map/bsp/convexsubspace.h \
include/map/bsp/edgetip.h \
include/map/bsp/hplane.h \
include/map/bsp/linesegment.h \
Expand Down Expand Up @@ -509,6 +510,7 @@ SOURCES += \
src/main_client.cpp \
src/map/blockmap.cpp \
src/map/blockmapvisual.cpp \
src/map/bsp/convexsubspace.cpp \
src/map/bsp/hplane.cpp \
src/map/bsp/linesegment.cpp \
src/map/bsp/partitioner.cpp \
Expand Down
129 changes: 129 additions & 0 deletions doomsday/client/include/map/bsp/convexsubspace.h
@@ -0,0 +1,129 @@
/** @file map/bsp/convexsubspace.h BSP Builder Convex Subspace.
*
* @authors Copyright © 2013 Daniel Swanson <danij@dengine.net>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA</small>
*/

#ifndef DENG_WORLD_MAP_BSP_CONVEXSUBSPACE
#define DENG_WORLD_MAP_BSP_CONVEXSUBSPACE

#include <QSet>

#include <de/Error>

#include "map/bsp/linesegment.h"

class BspLeaf;
class Sector;

namespace de {
namespace bsp {

/**
* Models a @em logical convex subspace in the partition plane, providing the
* analysis functionality necessary to classify and then separate the segments
* into unique geometries.
*
* Here infinity (i.e., a subspace containing no segments) is considered to be
* convex. Accordingly any segments linked to the subspace are @em not "owned"
* by it.
*
* @note Important: It is the user's responsibility to ensure convexity else
* many of the operations on the set of segments will be illogical.
*
* @ingroup bsp
*/
class ConvexSubspace
{
/// The set of line segments.
typedef QSet<LineSegment::Side *> Segments;

public:
/**
* Construct an empty convex subspace.
*/
ConvexSubspace();

/**
* Construct a convex subspace from a list of line @a segments.
*
* @param segments List of line segments which are assumed to define a
* convex subspace in the plane. Ownership of the segments
* is @em NOT given to the subspace. Note that duplicates
* are pruned automatically.
*/
ConvexSubspace(QList<LineSegment::Side *> const &segments);

ConvexSubspace(ConvexSubspace const &other);

/**
* Add more line segments to the subspace. It is assumed that the new set
* conforms to, or is compatible with the subspace.
*
* @param segments List of line segments to add to the subspace. Ownership
* of the segments is @em NOT given to the subspace. Note
* that duplicates or any which are already present are
* pruned automatically.
*/
void addSegments(QList<LineSegment::Side *> const &segments);

/**
* Add a single line segment to the subspace which is assumed to conform to,
* or is compatible with the subspace.
*
* @param segment Line segment to add. Ownership is @em NOT given to the
* subspace. Note that if the segment is already present in
* the set it will be pruned (nothing will happen).
*/
void addOneSegment(LineSegment::Side const &segment);

/**
* Determines from the set of line segments which set to attribute to any
* BSP leaf we might subsequently produce for them.
*
* This choice is determined with a heuristic accounting of the number of
* references to each candidate sector. References are divided into groups
* according to the "type" of the referencing line segment for rating.
*/
Sector *chooseSectorForBspLeaf() const;

/**
* The BspLeaf to which the subspace has been attributed. May return @c 0
* if not attributed.
*/
BspLeaf *bspLeaf() const;

/**
* Change the BspLeaf to which the subspace is attributed.
*
* @param newBspLeaf BSP leaf to attribute (ownership is not affected).
* Can be @c 0 (to clear the attribution).
*/
void setBspLeaf(BspLeaf *newBspLeaf);

/**
* Provides access to the set line segments for efficient traversal.
*/
Segments const &segments() const;

private:
DENG2_PRIVATE(d)
};

} // namespace bsp
} // namespace de

#endif // DENG_WORLD_MAP_BSP_CONVEXSUBSPACE
253 changes: 253 additions & 0 deletions doomsday/client/src/map/bsp/convexsubspace.cpp
@@ -0,0 +1,253 @@
/** @file map/bsp/convexsubspace.cpp BSP Builder Convex Subspace.
*
* @authors Copyright © 2013 Daniel Swanson <danij@dengine.net>
*
* @par License
* GPL: http://www.gnu.org/licenses/gpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details. You should have received a copy of the GNU
* General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA</small>
*/

#include <QHash>

#include "Line"
#include "Sector"
#include "map/bsp/linesegment.h"

#include "render/r_main.h" /// validCount @todo Remove me

#include "map/bsp/convexsubspace.h"

namespace de {
namespace bsp {

/**
* Represents a choice of sector for BSP leaf attribution.
*/
struct Choice
{
/// The sector choice.
Sector *sector;

/// Number of referencing line segments of each type:
int norm;
int part;
int self;

Choice(Sector &sector)
: sector(&sector), norm(0), part(0), self(0)
{}

/**
* Perform heuristic comparison between two choices to determine
* a preference order. The algorithm used weights the two choices
* according to the number and "type" of the referencing line
* segments.
*
* @return @c true if "this" choice is rated better than @a other.
*/
bool operator < (Choice const &other) const
{
if(norm == other.norm)
{
if(part == other.part)
{
if(self == other.self)
{
// All equal; use the unique indices to stablize.
return sector->indexInMap() < other.sector->indexInMap();
}
return self > other.self;
}
return part > other.part;
}
return norm > other.norm;
}

/**
* Account for a new line segment which references this choice.
* Consider collinear segments only once.
*/
void account(LineSegment::Side &seg)
{
// Determine the type of reference and increment the count.
if(!seg.hasMapSide())
{
Line *mapLine = seg.partitionMapLine();
if(mapLine && mapLine->validCount() == validCount)
return;

part += 1;

if(mapLine)
mapLine->setValidCount(validCount);
}
else
{
if(seg.mapLine().validCount() == validCount)
return;

if(seg.mapLine().isSelfReferencing())
{
self += 1;
}
else
{
norm += 1;
}

seg.mapLine().setValidCount(validCount);
}
}
};
typedef QHash<Sector *, Choice> ChoiceHash;

DENG2_PIMPL_NOREF(ConvexSubspace)
{
/// The set of line segments.
Segments segments;

/// Chosen map sector for this subspace (if any).
Sector *sector;

/// Set to @c true when should to rethink our chosen sector.
bool needChooseSector;

/// BSP leaf attributed to the subspace (if any).
BspLeaf *bspLeaf;

Instance()
: sector(0),
needChooseSector(true),
bspLeaf(0)
{}

Instance(Instance const &other)
: de::IPrivate(),
segments (other.segments),
sector (other.sector),
needChooseSector(other.needChooseSector),
bspLeaf (other.bspLeaf)
{}

void chooseSector()
{
sector = 0;

// We will consider collinear segments only once.
validCount++;

ChoiceHash candidates;
foreach(LineSegment::Side *seg, segments)
{
// Segments with no sector can't help us.
if(!seg->hasSector()) continue;

Sector *cand = seg->sectorPtr();

// Is this a new choice?
ChoiceHash::iterator found = candidates.find(cand);
if(found == candidates.end())
{
// Yes, record it.
found = candidates.insert(sector, Choice(*cand));
}

// Account for a new segment referencing this sector.
found.value().account(*seg);
}

if(candidates.isEmpty()) return; // Eeek!

// Sort our choices such that our preferred sector appears first. This
// shouldn't take too long, typically there is no more than two or three
// to choose from.
QList<Choice> sortedCandidates = candidates.values();
qSort(sortedCandidates.begin(), sortedCandidates.end());

// We'll choose the highest rated choice.
sector = sortedCandidates.first().sector;

needChooseSector = false;
}

private:
Instance &operator = (Instance const &); // no assignment
};

ConvexSubspace::ConvexSubspace()
: d(new Instance())
{}

ConvexSubspace::ConvexSubspace(QList<LineSegment::Side *> const &segments)
: d(new Instance())
{
addSegments(segments);
}

ConvexSubspace::ConvexSubspace(ConvexSubspace const &other)
: d(new Instance(*other.d))
{}

void ConvexSubspace::addSegments(QList<LineSegment::Side *> const &segments)
{
int sizeBefore = d->segments.size();

d->segments.unite(segments.toSet());

if(d->segments.size() != sizeBefore)
{
// We'll need to rethink our sector choice.
d->needChooseSector = true;
}
}

void ConvexSubspace::addOneSegment(LineSegment::Side const &segment)
{
int sizeBefore = d->segments.size();

d->segments.insert(const_cast<LineSegment::Side *>(&segment));

if(d->segments.size() != sizeBefore)
{
// We'll need to rethink our sector choice.
d->needChooseSector = true;
}
}

Sector *ConvexSubspace::chooseSectorForBspLeaf() const
{
// Do we need to rethink our choice?
if(d->needChooseSector)
{
d->chooseSector();
}
return d->sector;
}

BspLeaf *ConvexSubspace::bspLeaf() const
{
return d->bspLeaf;
}

void ConvexSubspace::setBspLeaf(BspLeaf *newBspLeaf)
{
d->bspLeaf = newBspLeaf;
}

ConvexSubspace::Segments const &ConvexSubspace::segments() const
{
return d->segments;
}

} // namespace bsp
} // namespace de

0 comments on commit 5b5c3e2

Please sign in to comment.