Skip to content

Commit

Permalink
World|Line: Line::Side has ownership Segments; basic API for management
Browse files Browse the repository at this point in the history
In the future this mechanism will be replaced entirely, so for now
this simple implementation will suffice.

Todo for later: Both BspLeaf and Line::Side now implement a similar
mechanism for management of "subelement geometries". This mechanism
should be generalized with a uniform (likely URI based) method of
addressing geometries (libdeng2's Bank seems to be a good fit).
  • Loading branch information
danij-deng committed Aug 12, 2013
1 parent d97e78e commit 2eb6693
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 84 deletions.
51 changes: 35 additions & 16 deletions doomsday/client/include/world/line.h
Expand Up @@ -21,11 +21,12 @@
#ifndef DENG_WORLD_LINE_H
#define DENG_WORLD_LINE_H

#include <QFlags>
#include <QList>

#include <de/binangle.h>
#include <de/vector1.h> /// @todo remove me

#include <QFlags>

#include <de/Error>
#include <de/Observers>
#include <de/Vector>
Expand Down Expand Up @@ -153,6 +154,11 @@ class Line : public de::MapElement
*/
inline Line &line() const { return lineSide().line(); }

/**
* Returns the half-edge for the segment.
*/
de::HEdge &hedge() const;

#ifdef __CLIENT__

/**
Expand Down Expand Up @@ -213,6 +219,8 @@ class Line : public de::MapElement
DENG2_PRIVATE(d)
};

typedef QList<Segment *> Segments;

public:
Side(Line &line, Sector *sector = 0);

Expand Down Expand Up @@ -426,32 +434,43 @@ class Line : public de::MapElement
inline Sector *sectorPtr() const { return hasSector()? &sector() : 0; }

/**
* Returns a pointer the left-most half-edge for the side; otherwise @c 0.
* Clears (destroys) all segments for the side.
*/
de::HEdge *leftHEdge() const;
void clearSegments();

/**
* Change the left-most half-ege for the side.
* Create a Segment for the specified half-edge. If an existing Segment
* is present for the half-edge it will be returned instead (nothing will
* happen).
*
* @param newHEdge New half-edge to set as the left-most. Can be @c 0.
* It is assumed that the half-edge is collinear with and represents a
* subsection of the line geometry. It is also assumed that the half-edge
* faces the same direction as this side. It is the caller's responsibility
* to ensure these two requirements are met otherwise the segment list
* will be ordered illogically.
*
* @todo Refactor away. Only needed presently because of Polyobj.
* @param hedge Half-edge to create a new Segment for.
*
* @return Pointer to the (possibly newly constructed) Segment.
*/
void setLeftHEdge(de::HEdge *newHEdge);
Segment *addSegment(de::HEdge &hedge);

/**
* Returns a pointer to the right-most half-edge for the side; otherwise @c 0.
* Provides access to the sorted segment list for the side.
*/
de::HEdge *rightHEdge() const;
Segments const &segments() const;

/**
* Change the right-most half-edge for the side.
*
* @param newHEdge New half-edge to set as the right-most. Can be @c 0.
*
* @todo Refactor away. Only needed presently because of Polyobj.
* Convenient method of returning the half-edge of the left-most segment
* on this side of the line; otherwise @c 0 (no segments exist).
*/
void setRightHEdge(de::HEdge *newHEdge);
de::HEdge *leftHEdge() const;

/**
* Convenient method of returning the half-edge of the right-most segment
* on this side of the line; otherwise @c 0 (no segments exist).
*/
de::HEdge *rightHEdge() const;

/**
* Update the tangent space normals of the side's surfaces according to the
Expand Down
4 changes: 3 additions & 1 deletion doomsday/client/src/render/rend_main.cpp
Expand Up @@ -53,10 +53,12 @@
#include "BspLeaf"
#include "BspNode"

#include "HueCircleVisual"
#include "BiasIllum"
#include "BiasSurface"
#include "SkyFixEdge"
#include "WallEdge"
#include "TriangleStripBuilder"
#include "HueCircleVisual"
#include "render/blockmapvisual.h"
#include "render/sprite.h"

Expand Down
20 changes: 8 additions & 12 deletions doomsday/client/src/world/bsp/convexsubspace.cpp
Expand Up @@ -427,15 +427,13 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf, Mesh &mesh) const

if(Line::Side *mapSide = lineSeg->mapSidePtr())
{
Line::Side::Segment *seg = new Line::Side::Segment(*mapSide, *hedge);

// Attribute the segment to half-edge.
hedge->setMapElement(seg);

Line::Side::Segment *seg = mapSide->addSegment(*hedge);
#ifdef __CLIENT__
/// @todo Line::Side::newSegment() should encapsulate:
seg->setLineSideOffset(Vector2d(mapSide->from().origin() - lineSeg->from().origin()).length());

seg->setLength(Vector2d(lineSeg->to().origin() - lineSeg->from().origin()).length());
#else
DENG_UNUSED(seg);
#endif
}

Expand Down Expand Up @@ -523,15 +521,13 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf, Mesh &mesh) const

if(Line::Side *mapSide = lineSeg->mapSidePtr())
{
Line::Side::Segment *seg = new Line::Side::Segment(*mapSide, *hedge);

// Attribute the segment to the half-edge.
hedge->setMapElement(seg);

Line::Side::Segment *seg = mapSide->addSegment(*hedge);
#ifdef __CLIENT__
/// @todo Line::Side::newSegment() should encapsulate:
seg->setLineSideOffset(Vector2d(mapSide->from().origin() - lineSeg->from().origin()).length());

seg->setLength(Vector2d(lineSeg->to().origin() - lineSeg->from().origin()).length());
#else
DENG_UNUSED(seg);
#endif
}

Expand Down
27 changes: 0 additions & 27 deletions doomsday/client/src/world/bsp/partitioner.cpp
Expand Up @@ -1514,33 +1514,6 @@ BspTreeNode *Partitioner::buildBsp(LineSet const &lines, Mesh &mesh)
d->splitOverlappingLineSegments();
d->buildLeafGeometries();

// Find the half-edges at the edge of each map line side.
/// @todo Optimize: Performing a search for both sides of the same map
/// line should be unnecessary provided we produced a complete tree with
/// no degenerate leaf geometries...
foreach(LineSegment *lineSeg, d->lineSegments)
for(int i = 0; i < 2; ++i)
{
LineSegment::Side &seg = lineSeg->side(i);

if(!seg.hasMapSide()) continue;
if(!seg.hasHEdge()) continue; // Oh dear...

// Find the left-most segment.
LineSegment::Side *left = &seg;
while(left->hasLeft() && left->left().hasHEdge())
{ left = &left->left(); }

seg.mapSide().setLeftHEdge(left->hedgePtr());

// Find the right-most segment.
LineSegment::Side *right = &seg;
while(right->hasRight() && right->right().hasHEdge())
{ right = &right->right(); }

seg.mapSide().setRightHEdge(right->hedgePtr());
}

return d->rootNode;
}

Expand Down
101 changes: 78 additions & 23 deletions doomsday/client/src/world/line.cpp
Expand Up @@ -19,9 +19,7 @@
*/

#include <QMap>

#include <de/libdeng2.h>
#include <de/Vector>
#include <QtAlgorithms>

#include "m_misc.h"

Expand Down Expand Up @@ -181,6 +179,12 @@ Line::Side &Line::Side::Segment::lineSide() const
return *this->parent().as<Line::Side>();
}

HEdge &Line::Side::Segment::hedge() const
{
DENG_ASSERT(d->hedge != 0);
return *d->hedge;
}

#ifdef __CLIENT__

coord_t Line::Side::Segment::lineSideOffset() const
Expand Down Expand Up @@ -299,23 +303,25 @@ DENG2_PIMPL_NOREF(Line::Side)
/// Attributed sector (not owned).
Sector *sector;

/// Left-most half-egde on this side of the owning line (not owned).
HEdge *leftHEdge;

/// Right-most half-edge on this side of the owning line (not owned).
HEdge *rightHEdge;
/// The sorted list of segments on this side.
Line::Side::Segments segments;
bool needSortSegments; ///< set to @c true when the list needs sorting.

/// Framecount of last time shadows were drawn on this side.
int shadowVisCount;

Instance(Sector *sector)
: flags(0),
sector(sector),
leftHEdge(0),
rightHEdge(0),
needSortSegments(false),
shadowVisCount(0)
{}

~Instance()
{
qDeleteAll(segments);
}

/**
* Retrieve the Section associated with @a sectionId.
*/
Expand All @@ -334,6 +340,22 @@ DENG2_PIMPL_NOREF(Line::Side)
throw Line::InvalidSectionIdError("Line::Side::section", QString("Invalid section id %1").arg(sectionId));
}

void sortSegments(Vector2d lineSideOrigin)
{
needSortSegments = false;

if(segments.count() < 2)
return;

// We'll use a QMap for sorting the segments.
QMap<coord_t, Segment *> sortedSegs;
foreach(Segment *seg, segments)
{
sortedSegs.insert((seg->hedge().origin() - lineSideOrigin).length(), seg);
}
segments = sortedSegs.values();
}

#ifdef __CLIENT__
/// Observes Line FlagsChange
void lineFlagsChanged(Line &line, int oldFlags)
Expand All @@ -351,7 +373,6 @@ DENG2_PIMPL_NOREF(Line::Side)
}
}
#endif

};

Line::Side::Side(Line &line, Sector *sector)
Expand Down Expand Up @@ -385,13 +406,12 @@ bool Line::Side::considerOneSided() const
{
// If no segment is linked then the convex subspace on "this" side must
// have been degenerate (thus no geometry).
if(!d->leftHEdge)
return true;
HEdge *hedge = leftHEdge();

if(!d->leftHEdge->twin().hasFace())
if(!hedge || !hedge->twin().hasFace())
return true;

if(!d->leftHEdge->twin().face().mapElement()->as<BspLeaf>()->hasSector())
if(!hedge->twin().face().mapElement()->as<BspLeaf>()->hasSector())
return true;
}

Expand Down Expand Up @@ -446,24 +466,59 @@ ddmobj_base_t const &Line::Side::soundEmitter(int sectionId) const
return const_cast<ddmobj_base_t const &>(const_cast<Side *>(this)->soundEmitter(sectionId));
}

HEdge *Line::Side::leftHEdge() const
void Line::Side::clearSegments()
{
return d->leftHEdge;
d->segments.clear();
d->needSortSegments = false; // An empty list is sorted.
}

void Line::Side::setLeftHEdge(HEdge *newHEdge)
Line::Side::Segment *Line::Side::addSegment(de::HEdge &hedge)
{
d->leftHEdge = newHEdge;
// Have we an exiting segment for this half-edge?
foreach(Segment *seg, d->segments)
{
if(&seg->hedge() == &hedge)
return seg;
}

// No, insert a new one.
Segment *newSeg = new Segment(*this, hedge);
d->segments.append(newSeg);
d->needSortSegments = true; // We'll need to (re)sort.

// Attribute the segment to half-edge.
hedge.setMapElement(newSeg);

return newSeg;
}

HEdge *Line::Side::rightHEdge() const
Line::Side::Segments const &Line::Side::segments() const
{
if(d->needSortSegments)
{
d->sortSegments(from().origin());
}
return d->segments;
}

HEdge *Line::Side::leftHEdge() const
{
return d->rightHEdge;
if(d->segments.isEmpty()) return 0;
if(d->needSortSegments)
{
d->sortSegments(from().origin());
}
return &d->segments.first()->hedge();
}

void Line::Side::setRightHEdge(HEdge *newHEdge)
HEdge *Line::Side::rightHEdge() const
{
d->rightHEdge = newHEdge;
if(d->segments.isEmpty()) return 0;
if(d->needSortSegments)
{
d->sortSegments(from().origin());
}
return &d->segments.last()->hedge();
}

void Line::Side::updateSoundEmitterOrigin(int sectionId)
Expand Down
9 changes: 4 additions & 5 deletions doomsday/client/src/world/map.cpp
Expand Up @@ -3521,13 +3521,12 @@ bool Map::endEditing()
hedge->setTwin(polyobj->mesh().newHEdge(line->to()));
hedge->twin().setTwin(hedge);

hedge->setMapElement(new Line::Side::Segment(line->front(), *hedge));
Line::Side::Segment *seg = line->front().addSegment(*hedge);
#ifdef __CLIENT__
hedge->mapElement()->as<Line::Side::Segment>()->setLength(line->length());
seg->setLength(line->length());
#else
DENG_UNUSED(seg);
#endif

line->front().setLeftHEdge(hedge);
line->front().setRightHEdge(hedge);
}

polyobj->buildUniqueVertexes();
Expand Down

0 comments on commit 2eb6693

Please sign in to comment.