diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index 73ee2a8635..e9857c24d1 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -113,6 +113,7 @@ DENG_HEADERS += \ include/BspLeaf \ include/BspNode \ include/EntityDatabase \ + include/Face \ include/Game \ include/Games \ include/HEdge \ @@ -244,6 +245,7 @@ DENG_HEADERS += \ include/map/bspnode.h \ include/map/dam_file.h \ include/map/entitydatabase.h \ + include/map/face.h \ include/map/gamemap.h \ include/map/generators.h \ include/map/hedge.h \ @@ -522,6 +524,7 @@ SOURCES += \ src/map/bspnode.cpp \ src/map/dam_file.cpp \ src/map/entitydatabase.cpp \ + src/map/face.cpp \ src/map/gamemap.cpp \ src/map/generators.cpp \ src/map/hedge.cpp \ diff --git a/doomsday/client/include/Face b/doomsday/client/include/Face new file mode 100644 index 0000000000..b63d8109eb --- /dev/null +++ b/doomsday/client/include/Face @@ -0,0 +1 @@ +#include "map/face.h" diff --git a/doomsday/client/include/map/bspleaf.h b/doomsday/client/include/map/bspleaf.h index 963d70e058..e686a8cc69 100644 --- a/doomsday/client/include/map/bspleaf.h +++ b/doomsday/client/include/map/bspleaf.h @@ -27,6 +27,7 @@ #include #include "MapElement" +#include "HEdge" #include "Line" #include "Polygon" diff --git a/doomsday/client/include/map/face.h b/doomsday/client/include/map/face.h new file mode 100644 index 0000000000..47b0987276 --- /dev/null +++ b/doomsday/client/include/map/face.h @@ -0,0 +1,128 @@ +/** @file map/face.h World Map Face Geometry. + * + * @authors Copyright © 2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * 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 + */ + +#ifndef DENG_WORLD_MAP_FACE +#define DENG_WORLD_MAP_FACE + +#include + +#include + +#include "MapElement" + +namespace de { + +class HEdge; +class Polygon; + +/** + * Face geometry. + * + * @ingroup map + */ +class Face +{ + DENG2_NO_COPY (Face) + DENG2_NO_ASSIGN(Face) + +public: + explicit Face(Polygon &poly); + + /** + * Returns the Polygon mesh the face is a part of. + */ + Polygon &poly() const; + + /** + * Returns a pointer to the first half-edge in the face geometry (note that + * half-edges are sorted in a clockwise order). May return @c 0 if there is + * no half-edge linked to the face. + */ + HEdge *hedge() const; + + /** + * Change the first half-edge in the face geometry. + */ + void setHEdge(HEdge *newHEdge); + + /** + * Returns the axis-aligned bounding box which encompases all the vertexes + * which define the face geometry. + */ + AABoxd const &aaBox() const; + + /** + * Update the face geometry's axis-aligned bounding box to encompass all vertexes. + */ + void updateAABox(); + + /** + * Returns the point described by the average origin coordinates of all the + * vertexes which define the geometry. + */ + Vector2d const ¢er() const; + + /** + * Update the center point of the geometry. + * + * @pre Axis-aligned bounding box must have been initialized. + */ + void updateCenter(); + + /** + * Determines whether the face geometry is currently convex. + * + * @note Due to the potential computational complexity of determining convexity + * this should be called sparingly/only when necessary. + * + * @todo Cache this result. + */ + bool isConvex() const; + + /** + * Returns a pointer to the map element attributed to the face. May return @c 0 + * if not attributed. + */ + MapElement *mapElement() const; + + /** + * Change the MapElement to which the face is attributed. + * + * @param newMapElement New MapElement to attribute to the face. Ownership is + * unaffected. Can be @c 0 (to clear the attribution). + * + * @see mapElement() + */ + void setMapElement(MapElement *newMapElement); + +#ifdef DENG_DEBUG + /** + * Output a textual, human-readable description/representation of the face to + * the application's output log. + */ + void print() const; +#endif + +private: + DENG2_PRIVATE(d) +}; + +} // namespace de + +#endif // DENG_WORLD_MAP_FACE diff --git a/doomsday/client/include/map/hedge.h b/doomsday/client/include/map/hedge.h index 4f2a88b32d..acbac3ff39 100644 --- a/doomsday/client/include/map/hedge.h +++ b/doomsday/client/include/map/hedge.h @@ -28,7 +28,7 @@ namespace de { -class Polygon; +class Face; /** * Half-edge geometry. @@ -47,8 +47,8 @@ class HEdge /// Required neighbor half-edge is missing. @ingroup errors DENG2_ERROR(MissingNeighborError); - /// Required polygon is missing. @ingroup errors - DENG2_ERROR(MissingPolygonError); + /// Required face is missing. @ingroup errors + DENG2_ERROR(MissingFaceError); public: HEdge(Vertex &vertex); @@ -175,26 +175,26 @@ class HEdge void setTwin(HEdge const *newTwin); /** - * Returns @c true iff the half-edge is part of some Polygon. + * Returns @c true iff the half-edge is part of some Face geometry. */ - bool hasPoly() const; + bool hasFace() const; /** - * Returns the Polygon the half-edge is a part of. + * Returns the Face geometry the half-edge is a part of. * - * @see hasPoly() + * @see hasFace() */ - Polygon &poly() const; + Face &face() const; /** - * Change the Polygon to which the half-edge is attributed. + * Change the Face to which the half-edge is attributed. * - * @param newPolygon New Polygon to attribute to the half-edge. Ownership is - * unaffected. Can be @c 0 (to clear the attribution). + * @param newFace New Face to attribute to the half-edge. Ownership is + * unaffected. Can be @c 0 (to clear the attribution). * - * @see hasPoly(), poly() + * @see hasFace(), face() */ - void setPoly(Polygon const *newPolygon); + void setFace(Face const *newFace); /** * Returns a pointer to the map element attributed to the half-edge. May return diff --git a/doomsday/client/include/map/polygon.h b/doomsday/client/include/map/polygon.h index 7ab314295d..fc9a8d3f4e 100644 --- a/doomsday/client/include/map/polygon.h +++ b/doomsday/client/include/map/polygon.h @@ -1,4 +1,4 @@ -/** @file map/polygon.h World Map Polygon Geometry. +/** @file data/polygon.h Polygon Mesh Geometry. * * @authors Copyright © 2003-2013 Jaakko Keränen * @authors Copyright © 2006-2013 Daniel Swanson @@ -18,122 +18,43 @@ * 02110-1301 USA */ -#ifndef DENG_WORLD_MAP_POLYGON -#define DENG_WORLD_MAP_POLYGON +#ifndef DENG_DATA_POLYGON +#define DENG_DATA_POLYGON -#include - -#include -#include - -class BspLeaf; +#include namespace de { -class HEdge; +class Face; /** - * Polygon geometry. + * Polygon mesh geometry. * - * @ingroup map + * @ingroup data */ class Polygon { -public: - /// No BSP leaf is attributed. @ingroup errors - DENG2_ERROR(MissingBspLeafError); - -public: /// @todo Make private: - /// First half-edge in the face geometry. Ordered by angle, clockwise starting - /// from the smallest angle. - HEdge *_hedge; - - /// Number of half-edge's in the face. +public: /// @todo make private: + /// Total number of half-edge's in the polygon. int _hedgeCount; public: Polygon(); /** - * Returns @c true iff a BSP leaf is attributed to the polygon. - */ - bool hasBspLeaf() const; - - /** - * Returns the BSP leaf attributed to the polygon. - * - * @see hasBspLeaf() + * Returns a pointer to the first Face of the polygon. */ - BspLeaf &bspLeaf() const; - - /** - * Change the BSP leaf attributed to the polygon. - * - * @param newBspLeaf New BSP leaf to attribute the polygon. Ownership is - * unaffected. Can be @c 0 (to clear the attribution). - * - * @see hasBspLeaf(), bspLeaf() - */ - void setBspLeaf(BspLeaf *newBspLeaf); - - /** - * Returns a pointer to the first half-edge of the Face of the polygon (note - * that half-edges are sorted in a clockwise order). May return @c 0 if there - * is no half-edge linked to the face. - */ - HEdge *firstHEdge() const; + Face *firstFace() const; /** * Total number of half-edges in the polygon. */ int hedgeCount() const; - /** - * Returns the axis-aligned bounding box which encompases all the vertexes - * which define the geometry. - */ - AABoxd const &aaBox() const; - - /** - * Update the geometry's axis-aligned bounding box to encompass all vertexes. - */ - void updateAABox(); - - /** - * Returns the point described by the average origin coordinates of all the - * vertexes which define the geometry. - */ - Vector2d const ¢er() const; - - /** - * Update the center point of the geometry. - * - * @pre Axis-aligned bounding box must have been initialized. - */ - void updateCenter(); - - /** - * Determines whether the geometry of the polygon is currently convex. - * - * @note Due to the potential computational complexity of determining convexity - * this should be called sparingly/only when necessary. - * - * @todo Cache this result. - */ - bool isConvex() const; - -#ifdef DENG_DEBUG - /** - * Output a textual, human-readable description/representation of the polygon - * to the application's output log. - */ - void print() const; -#endif - private: DENG2_PRIVATE(d) }; } // namespace de -#endif // DENG_WORLD_MAP_POLYGON +#endif // DENG_DATA_POLYGON diff --git a/doomsday/client/include/map/segment.h b/doomsday/client/include/map/segment.h index 28a7e2dd41..799c3bab8d 100644 --- a/doomsday/client/include/map/segment.h +++ b/doomsday/client/include/map/segment.h @@ -24,16 +24,16 @@ #include #include "MapElement" +#include "BspLeaf" +#include "Face" #include "HEdge" #include "Line" -#include "Polygon" #include "Vertex" #ifdef __CLIENT__ struct BiasSurface; #endif -class BspLeaf; class Sector; /** @@ -89,7 +89,7 @@ class Segment : public de::MapElement * @see HEdge::hasPoly(), Polygon::hasBspLeaf() */ inline bool hasBspLeaf() const { - return hedge().hasPoly() && hedge().poly().hasBspLeaf(); + return hedge().hasFace() && hedge().face().mapElement() != 0; } /** @@ -98,7 +98,9 @@ class Segment : public de::MapElement * * @see hasBspLeaf(), Polygon::bspLeaf() */ - inline BspLeaf &bspLeaf() const { return hedge().poly().bspLeaf(); } + inline BspLeaf &bspLeaf() const { + return *hedge().face().mapElement()->castTo(); + } /** * Convenience accessor which returns the Sector attributed to the BspLeaf diff --git a/doomsday/client/src/audio/s_environ.cpp b/doomsday/client/src/audio/s_environ.cpp index c763424c6b..10baf3c536 100644 --- a/doomsday/client/src/audio/s_environ.cpp +++ b/doomsday/client/src/audio/s_environ.cpp @@ -169,10 +169,10 @@ static void findBspLeafsAffectingSector(GameMap *map, Sector *sec) // Is this BSP leaf close enough? if(bspLeaf->sectorPtr() == sec || // leaf is IN this sector - (bspLeaf->poly().center().x > affectionBounds.minX && - bspLeaf->poly().center().y > affectionBounds.minY && - bspLeaf->poly().center().x < affectionBounds.maxX && - bspLeaf->poly().center().y < affectionBounds.maxY)) + (bspLeaf->poly().firstFace()->center().x > affectionBounds.minX && + bspLeaf->poly().firstFace()->center().y > affectionBounds.minY && + bspLeaf->poly().firstFace()->center().x < affectionBounds.maxX && + bspLeaf->poly().firstFace()->center().y < affectionBounds.maxY)) { // It will contribute to the reverb settings of this sector. setBspLeafSectorOwner(&bspLeafOwnerList, bspLeaf); @@ -245,8 +245,8 @@ static boolean calcBspLeafReverb(BspLeaf *bspLeaf) // Space is the rough volume of the BSP leaf (bounding box). bspLeaf->_reverb[SRD_SPACE] = (int) (bspLeaf->sector().ceiling().height() - bspLeaf->sector().floor().height()) * - (bspLeaf->poly().aaBox().maxX - bspLeaf->poly().aaBox().minX) * - (bspLeaf->poly().aaBox().maxY - bspLeaf->poly().aaBox().minY); + (bspLeaf->poly().firstFace()->aaBox().maxX - bspLeaf->poly().firstFace()->aaBox().minX) * + (bspLeaf->poly().firstFace()->aaBox().maxY - bspLeaf->poly().firstFace()->aaBox().minY); float total = 0; // The other reverb properties can be found out by taking a look at the diff --git a/doomsday/client/src/map/blockmapvisual.cpp b/doomsday/client/src/map/blockmapvisual.cpp index f5b4db90c7..fc20b2becb 100644 --- a/doomsday/client/src/map/blockmapvisual.cpp +++ b/doomsday/client/src/map/blockmapvisual.cpp @@ -80,10 +80,9 @@ static int rendBspLeaf(BspLeaf *bspLeaf, void * /*parameters*/) float length, dx, dy, normal[2], unit[2]; vec2f_t start, end; - de::Polygon const &poly = bspLeaf->poly(); - HEdge *firstHEdge = poly.firstHEdge(); + Face const &face = *bspLeaf->poly().firstFace(); - HEdge *hedge = firstHEdge; + HEdge *hedge = face.hedge(); do { V2f_Set(start, hedge->origin().x, hedge->origin().y); @@ -127,9 +126,9 @@ static int rendBspLeaf(BspLeaf *bspLeaf, void * /*parameters*/) GL_BlendMode(BM_NORMAL); } - // Draw a bounding box for the leaf's geometry. - V2f_Set(start, bspLeaf->poly().aaBox().minX, bspLeaf->poly().aaBox().minY); - V2f_Set(end, bspLeaf->poly().aaBox().maxX, bspLeaf->poly().aaBox().maxY); + // Draw a bounding box for the leaf's face geometry. + V2f_Set(start, face.aaBox().minX, face.aaBox().minY); + V2f_Set(end, face.aaBox().maxX, face.aaBox().maxY); glBegin(GL_LINES); glVertex2f(start[VX], start[VY]); @@ -142,7 +141,7 @@ static int rendBspLeaf(BspLeaf *bspLeaf, void * /*parameters*/) glVertex2f(start[VX], start[VY]); glEnd(); - } while((hedge = &hedge->next()) != firstHEdge); + } while((hedge = &hedge->next()) != face.hedge()); bspLeaf->setValidCount(validCount); } diff --git a/doomsday/client/src/map/bsp/convexsubspace.cpp b/doomsday/client/src/map/bsp/convexsubspace.cpp index 209ff2754a..18f0eef8ec 100644 --- a/doomsday/client/src/map/bsp/convexsubspace.cpp +++ b/doomsday/client/src/map/bsp/convexsubspace.cpp @@ -417,6 +417,7 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const { // Construct a new polygon and set of half-edges. Polygon *poly = new Polygon; + Face *face = poly->firstFace(); foreach(OrderedSegment const *oseg, conty.discordSegs) { @@ -442,8 +443,8 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const // Link the new half-edge for this line segment to the head of // the list in the new polygon geometry. - hedge->setNext(poly->_hedge); - poly->_hedge = hedge; + hedge->setNext(face->hedge()); + face->setHEdge(hedge); // Is there a half-edge on the back side we need to twin with? if(lineSeg->back().hasSegment()) @@ -460,15 +461,14 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const } // Link the half-edges anticlockwise and close the ring. - HEdge *hedge = poly->_hedge; + HEdge *hedge = face->hedge(); forever { // There is now one more half-edge in this polygon. poly->_hedgeCount += 1; - // Attribute the half edge to the Polygon. - /// @todo Encapsulate in Polygon. - hedge->setPoly(poly); + // Attribute the half edge to the Face. + hedge->setFace(face); if(hedge->hasNext()) { @@ -479,12 +479,16 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const else { // Circular link. - hedge->setNext(poly->_hedge); + hedge->setNext(face->hedge()); hedge->next().setPrev(hedge); break; } } + /// @todo Face should encapsulate. + face->updateAABox(); + face->updateCenter(); + // Attribute the new polygon to the BSP leaf. leaf.assignExtraPoly(poly); } @@ -510,6 +514,7 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const { // Construct the polygon and ring of half-edges. Polygon *poly = new Polygon; + Face *face = poly->firstFace(); // Iterate backwards so that the half-edges can be linked clockwise. for(int i = d->orderedSegments.size(); i-- > 0; ) @@ -539,9 +544,9 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const int( lineSeg->to().origin().x - lineSeg->from().origin().x )) << FRACBITS); // Link the new half-edge for this line segment to the head of - // the list in the new polygon geometry. - hedge->setNext(poly->_hedge); - poly->_hedge = hedge; + // the list in the new face geometry. + hedge->setNext(face->hedge()); + face->setHEdge(hedge); // Is there a half-edge on the back side we need to twin with? if(lineSeg->back().hasSegment()) @@ -558,15 +563,14 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const } // Link the half-edges anticlockwise and close the ring. - HEdge *hedge = poly->_hedge; + HEdge *hedge = face->hedge(); forever { // There is now one more half-edge in this polygon. poly->_hedgeCount += 1; - // Attribute the half edge to the Polygon. - /// @todo Encapsulate in Polygon. - hedge->setPoly(poly); + // Attribute the half edge to the Face. + hedge->setFace(face); if(hedge->hasNext()) { @@ -577,15 +581,15 @@ void ConvexSubspace::buildGeometry(BspLeaf &leaf) const else { // Circular link. - hedge->setNext(poly->_hedge); + hedge->setNext(face->hedge()); hedge->next().setPrev(hedge); break; } } - /// @todo Polygon should encapsulate. - poly->updateAABox(); - poly->updateCenter(); + /// @todo Face should encapsulate. + face->updateAABox(); + face->updateCenter(); // Assign the polygon geometry to the BSP leaf (takes ownership). leaf.assignPoly(poly); diff --git a/doomsday/client/src/map/bspleaf.cpp b/doomsday/client/src/map/bspleaf.cpp index b17da49e28..98205ce718 100644 --- a/doomsday/client/src/map/bspleaf.cpp +++ b/doomsday/client/src/map/bspleaf.cpp @@ -28,8 +28,9 @@ #include -#include "Segment" +#include "Polygon" #include "Polyobj" +#include "Segment" #include "Sector" #include "Vertex" @@ -139,38 +140,39 @@ DENG2_PIMPL(BspLeaf) #ifdef DENG2_QT_4_7_OR_NEWER clockwiseSegments.reserve(polygon->hedgeCount()); #endif - HEdge *firstHEdge = polygon->firstHEdge(); - HEdge *hedge = firstHEdge; + Face const &face = *polygon->firstFace(); + + HEdge *hedge = face.hedge(); do { if(MapElement *elem = hedge->mapElement()) { clockwiseSegments.append(elem->castTo()); } - } while((hedge = &hedge->next()) != firstHEdge); + } while((hedge = &hedge->next()) != face.hedge()); #ifdef DENG_DEBUG // See if we received a partial geometry... { int discontinuities = 0; - HEdge *hedge = firstHEdge; + HEdge *hedge = face.hedge(); do { if(hedge->next().origin() != hedge->twin().origin()) { discontinuities++; } - } while((hedge = &hedge->next()) != firstHEdge); + } while((hedge = &hedge->next()) != face.hedge()); if(discontinuities) { - LOG_WARNING("Polygon geometry for BSP leaf [%p] (at %s) in sector %i " + LOG_WARNING("Face geometry for BSP leaf [%p] (at %s) in sector %i " "is not contiguous %i gaps/overlaps (%i half-edges).") << de::dintptr(&self) - << polygon->center().asText() + << face.center().asText() << sector->indexInArchive() << discontinuities << polygon->hedgeCount(); - polygon->print(); + face.print(); } } #endif @@ -201,15 +203,15 @@ DENG2_PIMPL(BspLeaf) foreach(Polygon *poly, extraPolygons) { - HEdge *firstHEdge = poly->firstHEdge(); - HEdge *hedge = firstHEdge; + Face const &face = *poly->firstFace(); + HEdge *hedge = face.hedge(); do { if(MapElement *elem = hedge->mapElement()) { allSegments.append(elem->castTo()); } - } while((hedge = &hedge->next()) != firstHEdge); + } while((hedge = &hedge->next()) != face.hedge()); } } @@ -237,7 +239,7 @@ DENG2_PIMPL(BspLeaf) { #define MIN_TRIANGLE_EPSILON (0.1) ///< Area - HEdge *firstNode = polygon->firstHEdge(); + HEdge *firstNode = polygon->firstFace()->hedge(); fanBase = firstNode; @@ -326,7 +328,7 @@ Polygon const &BspLeaf::poly() const void BspLeaf::assignPoly(Polygon *newPoly) { - if(newPoly && !newPoly->isConvex()) + if(newPoly && newPoly->hedgeCount() < 3 /*!newPoly->firstFace()->isConvex()*/) { /// @throw InvalidPolygonError Attempted to assign a non-convex polygon. throw InvalidPolygonError("BspLeaf::setPoly", "Non-convex polygons cannot be assigned"); @@ -337,16 +339,16 @@ void BspLeaf::assignPoly(Polygon *newPoly) if(newPoly) { - // Attribute the new polygon to "this" BSP leaf. - newPoly->setBspLeaf(this); + // Attribute the new face geometry to "this" BSP leaf. + newPoly->firstFace()->setMapElement(this); // We'll need to update segment lists. d->needUpdateClockwiseSegments = true; d->needUpdateAllSegments = true; // Update the world grid offset. - d->worldGridOffset = Vector2d(fmod(newPoly->aaBox().minX, 64), - fmod(newPoly->aaBox().maxY, 64)); + d->worldGridOffset = Vector2d(fmod(newPoly->firstFace()->aaBox().minX, 64), + fmod(newPoly->firstFace()->aaBox().maxY, 64)); } else { @@ -362,8 +364,8 @@ void BspLeaf::assignExtraPoly(de::Polygon *newPoly) if(d->extraPolygons.size() != sizeBefore) { - // Attribute the new polygon to "this" BSP leaf. - newPoly->setBspLeaf(this); + // Attribute the new face geometry to "this" BSP leaf. + newPoly->firstFace()->setMapElement(this); // We'll need to update the all segment list. d->needUpdateAllSegments = true; diff --git a/doomsday/client/src/map/face.cpp b/doomsday/client/src/map/face.cpp new file mode 100644 index 0000000000..b591c6ce1b --- /dev/null +++ b/doomsday/client/src/map/face.cpp @@ -0,0 +1,143 @@ +/** @file map/face.cpp World Map Face Geometry. + * + * @authors Copyright © 2013 Daniel Swanson + * + * @par License + * GPL: http://www.gnu.org/licenses/gpl.html + * + * 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, see: + * http://www.gnu.org/licenses + */ + +#include +#include /// @todo remove me + +#include + +#include "HEdge" +#include "Polygon" + +#include "Face" + +namespace de { + +DENG2_PIMPL_NOREF(Face) +{ + /// Polygon mesh which owns the face. + Polygon *poly; + + /// First half-edge in the face geometry. + HEdge *hedge; + + /// MapElement to which the half-edge is attributed (if any). + MapElement *mapElement; + + /// Vertex bounding box. + AABoxd aaBox; + + /// Center of vertices. + Vector2d center; + + Instance(Polygon &poly) + : poly(&poly), + hedge(0), + mapElement(0) + {} +}; + +Face::Face(Polygon &poly) + : d(new Instance(poly)) +{} + +Polygon &Face::poly() const +{ + DENG_ASSERT(d->poly != 0); + return *d->poly; +} + +HEdge *Face::hedge() const +{ + return d->hedge; +} + +void Face::setHEdge(HEdge *newHEdge) +{ + d->hedge = newHEdge; +} + +AABoxd const &Face::aaBox() const +{ + return d->aaBox; +} + +void Face::updateAABox() +{ + d->aaBox.clear(); + + if(!d->hedge) return; // Very odd... + + HEdge *hedgeIt = d->hedge; + V2d_InitBoxXY(d->aaBox.arvec2, hedgeIt->origin().x, hedgeIt->origin().y); + + while((hedgeIt = &hedgeIt->next()) != d->hedge) + { + V2d_AddToBoxXY(d->aaBox.arvec2, hedgeIt->origin().x, hedgeIt->origin().y); + } +} + +Vector2d const &Face::center() const +{ + return d->center; +} + +void Face::updateCenter() +{ + // The center is the middle of our AABox. + d->center = Vector2d(d->aaBox.min) + (Vector2d(d->aaBox.max) - Vector2d(d->aaBox.min)) / 2; +} + +bool Face::isConvex() const +{ + /// @todo Implement full conformance checking. + return true; //_hedgeCount > 2; +} + +MapElement *Face::mapElement() const +{ + return d->mapElement; +} + +void Face::setMapElement(MapElement *newMapElement) +{ + d->mapElement = newMapElement; +} + +#ifdef DENG_DEBUG +void Face::print() const +{ + if(!d->hedge) return; + + LOG_INFO("Half-edges:"); + HEdge const *hedgeIt = d->hedge; + do + { + HEdge const &hedge = *hedgeIt; + coord_t angle = M_DirectionToAngleXY(hedge.origin().x - d->center.x, + hedge.origin().y - d->center.y); + + LOG_INFO(" [%p]: Angle %1.6f %s -> %s") + << de::dintptr(&hedge) << angle + << hedge.origin().asText() << hedge.twin().origin().asText(); + + } while((hedgeIt = &hedgeIt->next()) != d->hedge); +} +#endif + +} // namespace de diff --git a/doomsday/client/src/map/gamemap.cpp b/doomsday/client/src/map/gamemap.cpp index f7d8a825d8..4e35f0281b 100644 --- a/doomsday/client/src/map/gamemap.cpp +++ b/doomsday/client/src/map/gamemap.cpp @@ -1151,7 +1151,7 @@ void GameMap::linkBspLeaf(BspLeaf &bspLeaf) // BspLeafs without sectors don't get in. if(!bspLeaf.hasSector()) return; - AABoxd aaBox = bspLeaf.poly().aaBox(); + AABoxd aaBox = bspLeaf.poly().firstFace()->aaBox(); BlockmapCellBlock cellBlock; Blockmap_CellBlock(bspLeafBlockmap, &cellBlock, &aaBox); @@ -1188,11 +1188,13 @@ static int blockmapCellBspLeafsIterator(void *object, void *context) ok = false; // Check the bounds. + AABoxd const &leafAABox = bspLeaf->poly().firstFace()->aaBox(); + if(args->box && - (bspLeaf->poly().aaBox().maxX < args->box->minX || - bspLeaf->poly().aaBox().minX > args->box->maxX || - bspLeaf->poly().aaBox().minY > args->box->maxY || - bspLeaf->poly().aaBox().maxY < args->box->minY)) + (leafAABox.maxX < args->box->minX || + leafAABox.minX > args->box->maxX || + leafAABox.minY > args->box->maxY || + leafAABox.maxY < args->box->minY)) ok = false; if(ok) diff --git a/doomsday/client/src/map/hedge.cpp b/doomsday/client/src/map/hedge.cpp index f106ddf303..7289666856 100644 --- a/doomsday/client/src/map/hedge.cpp +++ b/doomsday/client/src/map/hedge.cpp @@ -1,6 +1,6 @@ /** @file map/hedge.cpp World Map Geometry Half-Edge. * - * @authors Copyright © 2011-2013 Daniel Swanson + * @authors Copyright © 2011-2013 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -38,8 +38,8 @@ DENG2_PIMPL(HEdge) /// Previous half-edge (anticlockwise) around the @em face. HEdge *prev; - /// Polygon geometry to which the half-edge is attributed (if any). - Polygon *poly; + /// Face geometry to which the half-edge is attributed (if any). + Face *face; /// MapElement to which the half-edge is attributed (if any). MapElement *mapElement; @@ -50,7 +50,7 @@ DENG2_PIMPL(HEdge) twin(0), next(0), prev(0), - poly(0), + face(0), mapElement(0) {} @@ -109,24 +109,24 @@ void HEdge::setTwin(HEdge const *newTwin) d->twin = const_cast(newTwin); } -bool HEdge::hasPoly() const +bool HEdge::hasFace() const { - return d->poly != 0; + return d->face != 0; } -Polygon &HEdge::poly() const +Face &HEdge::face() const { - if(d->poly) + if(d->face) { - return *d->poly; + return *d->face; } - /// @throw MissingPolygonError Attempted with no Polygon attributed. - throw MissingPolygonError("HEdge::poly", "No polygon is attributed"); + /// @throw MissingFaceError Attempted with no Face attributed. + throw MissingFaceError("HEdge::face", "No face is attributed"); } -void HEdge::setPoly(Polygon const *newPolygon) +void HEdge::setFace(Face const *newFace) { - d->poly = const_cast(newPolygon); + d->face = const_cast(newFace); } MapElement *HEdge::mapElement() const diff --git a/doomsday/client/src/map/p_maputil.cpp b/doomsday/client/src/map/p_maputil.cpp index ca5f9e8ab3..d1921f6733 100644 --- a/doomsday/client/src/map/p_maputil.cpp +++ b/doomsday/client/src/map/p_maputil.cpp @@ -98,7 +98,7 @@ bool P_IsPointInBspLeaf(Vector2d const &point, BspLeaf const &bspLeaf) return false; // Obviously not. de::Polygon const &poly = bspLeaf.poly(); - HEdge const *firstHEdge = poly.firstHEdge(); + HEdge const *firstHEdge = poly.firstFace()->hedge(); HEdge const *hedge = firstHEdge; do diff --git a/doomsday/client/src/map/p_objlink.cpp b/doomsday/client/src/map/p_objlink.cpp index 9ca01047b2..e05d38f120 100644 --- a/doomsday/client/src/map/p_objlink.cpp +++ b/doomsday/client/src/map/p_objlink.cpp @@ -351,11 +351,13 @@ static void processSegment(Segment *seg, void *parameters) return; // Not eligible for spreading. } + AABoxd const &backLeafAABox = backLeaf.poly().firstFace()->aaBox(); + // Is the leaf on the back side outside the origin's AABB? - if(backLeaf.poly().aaBox().maxX <= parms->box[BOXLEFT] || - backLeaf.poly().aaBox().minX >= parms->box[BOXRIGHT] || - backLeaf.poly().aaBox().maxY <= parms->box[BOXBOTTOM] || - backLeaf.poly().aaBox().minY >= parms->box[BOXTOP]) + if(backLeafAABox.maxX <= parms->box[BOXLEFT] || + backLeafAABox.minX >= parms->box[BOXRIGHT] || + backLeafAABox.maxY <= parms->box[BOXBOTTOM] || + backLeafAABox.minY >= parms->box[BOXTOP]) return; // Too far from the object? @@ -515,13 +517,15 @@ static void spreadContactsForBspLeaf(objlinkblockmap_t &obm, BspLeaf const &bspL { DENG_ASSERT(!bspLeaf.isDegenerate()); + AABoxd const &leafAABox = bspLeaf.poly().firstFace()->aaBox(); + uint minBlock[2]; - toObjlinkBlockmapCell(obm, minBlock, bspLeaf.poly().aaBox().minX - maxRadius, - bspLeaf.poly().aaBox().minY - maxRadius); + toObjlinkBlockmapCell(obm, minBlock, leafAABox.minX - maxRadius, + leafAABox.minY - maxRadius); uint maxBlock[2]; - toObjlinkBlockmapCell(obm, maxBlock, bspLeaf.poly().aaBox().maxX + maxRadius, - bspLeaf.poly().aaBox().maxY + maxRadius); + toObjlinkBlockmapCell(obm, maxBlock, leafAABox.maxX + maxRadius, + leafAABox.maxY + maxRadius); for(uint y = minBlock[1]; y <= maxBlock[1]; ++y) for(uint x = minBlock[0]; x <= maxBlock[0]; ++x) diff --git a/doomsday/client/src/map/p_particle.cpp b/doomsday/client/src/map/p_particle.cpp index f76a9fd57d..d651da61a0 100644 --- a/doomsday/client/src/map/p_particle.cpp +++ b/doomsday/client/src/map/p_particle.cpp @@ -659,13 +659,15 @@ static void P_NewParticle(ptcgen_t *gen) if(!bspLeaf || bspLeaf->isDegenerate()) goto spawn_failed; + AABoxd const &leafAABox = bspLeaf->poly().firstFace()->aaBox(); + // Try a couple of times to get a good random spot. for(i = 0; i < 10; ++i) // Max this many tries before giving up. { - float x = bspLeaf->poly().aaBox().minX + - RNG_RandFloat() * (bspLeaf->poly().aaBox().maxX - bspLeaf->poly().aaBox().minX); - float y = bspLeaf->poly().aaBox().minY + - RNG_RandFloat() * (bspLeaf->poly().aaBox().maxY - bspLeaf->poly().aaBox().minY); + float x = leafAABox.minX + + RNG_RandFloat() * (leafAABox.maxX - leafAABox.minX); + float y = leafAABox.minY + + RNG_RandFloat() * (leafAABox.maxY - leafAABox.minY); pt->origin[VX] = FLT2FIX(x); pt->origin[VY] = FLT2FIX(y); diff --git a/doomsday/client/src/map/polygon.cpp b/doomsday/client/src/map/polygon.cpp index 3c0b4996f1..0ce30f886b 100644 --- a/doomsday/client/src/map/polygon.cpp +++ b/doomsday/client/src/map/polygon.cpp @@ -18,13 +18,8 @@ * 02110-1301 USA */ -#include -#include /// @todo remove me - -#include - -#include "BspLeaf" #include "HEdge" +#include "Face" #include "map/polygon.h" @@ -32,43 +27,36 @@ namespace de { DENG2_PIMPL(Polygon) { - /// Vertex bounding box in the map coordinate space. - AABoxd aaBox; - - /// Center of vertices. - Vector2d center; - - /// BSP leaf to which the polygon is attributed (if any). - BspLeaf *bspLeaf; + /// Face geometry. + Face face; Instance(Public *i) : Base(i), - bspLeaf(0) + face(self) {} ~Instance() { - // Clear the half-edges. - if(self._hedge) + // Clear all the half-edges. + if(HEdge *hedge = face.hedge()) { - HEdge *he = self._hedge; - if(!he->hasNext() || &he->next() == he) + if(!hedge->hasNext() || &hedge->next() == hedge) { - delete he; + delete hedge; } else { // Break the ring, if linked. - if(he->hasPrev()) + if(hedge->hasPrev()) { - he->prev().setNext(0); + hedge->prev().setNext(0); } - while(he) + while(hedge) { - HEdge *next = he->hasNext()? &he->next() : 0; - delete he; - he = next; + HEdge *next = hedge->hasNext()? &hedge->next() : 0; + delete hedge; + hedge = next; } } } @@ -76,32 +64,12 @@ DENG2_PIMPL(Polygon) }; Polygon::Polygon() - : _hedge(0), _hedgeCount(0), d(new Instance(this)) + : _hedgeCount(0), d(new Instance(this)) {} -bool Polygon::hasBspLeaf() const +Face *Polygon::firstFace() const { - return d->bspLeaf != 0; -} - -BspLeaf &Polygon::bspLeaf() const -{ - if(d->bspLeaf) - { - return *d->bspLeaf; - } - /// @throw MissingBspLeafError Attempted with no BSP leaf attributed. - throw MissingBspLeafError("Polygon::bspLeaf", "No BSP leaf is attributed"); -} - -void Polygon::setBspLeaf(BspLeaf *newBspLeaf) -{ - d->bspLeaf = newBspLeaf; -} - -HEdge *Polygon::firstHEdge() const -{ - return _hedge; + return &d->face; } int Polygon::hedgeCount() const @@ -109,64 +77,4 @@ int Polygon::hedgeCount() const return _hedgeCount; } -AABoxd const &Polygon::aaBox() const -{ - return d->aaBox; -} - -void Polygon::updateAABox() -{ - d->aaBox.clear(); - - HEdge *base = _hedge; - if(!base) return; // Very odd... - - HEdge *hedgeIt = base; - V2d_InitBoxXY(d->aaBox.arvec2, hedgeIt->origin().x, hedgeIt->origin().y); - - while((hedgeIt = &hedgeIt->next()) != base) - { - V2d_AddToBoxXY(d->aaBox.arvec2, hedgeIt->origin().x, hedgeIt->origin().y); - } -} - -Vector2d const &Polygon::center() const -{ - return d->center; -} - -void Polygon::updateCenter() -{ - // The center is the middle of our AABox. - d->center = Vector2d(d->aaBox.min) + (Vector2d(d->aaBox.max) - Vector2d(d->aaBox.min)) / 2; -} - -bool Polygon::isConvex() const -{ - /// @todo Implement full conformance checking. - return _hedgeCount > 2; -} - -#ifdef DENG_DEBUG -void Polygon::print() const -{ - HEdge const *base = _hedge; - if(!base) return; - - LOG_INFO("Half-edges:"); - HEdge const *hedgeIt = base; - do - { - HEdge const &hedge = *hedgeIt; - coord_t angle = M_DirectionToAngleXY(hedge.origin().x - d->center.x, - hedge.origin().y - d->center.y); - - LOG_INFO(" [%p]: Angle %1.6f %s -> %s") - << de::dintptr(&hedge) << angle - << hedge.origin().asText() << hedge.twin().origin().asText(); - - } while((hedgeIt = &hedgeIt->next()) != base); -} -#endif - } // namespace de diff --git a/doomsday/client/src/map/sector.cpp b/doomsday/client/src/map/sector.cpp index 922aae6ed5..567c2e4628 100644 --- a/doomsday/client/src/map/sector.cpp +++ b/doomsday/client/src/map/sector.cpp @@ -370,13 +370,15 @@ void Sector::updateAABox() { if(leaf->isDegenerate()) continue; + AABoxd const &leafAABox = leaf->poly().firstFace()->aaBox(); + if(!isFirst) { - V2d_UniteBox(d->aaBox.arvec2, leaf->poly().aaBox().arvec2); + V2d_UniteBox(d->aaBox.arvec2, leafAABox.arvec2); } else { - V2d_CopyBox(d->aaBox.arvec2, leaf->poly().aaBox().arvec2); + V2d_CopyBox(d->aaBox.arvec2, leafAABox.arvec2); isFirst = false; } } @@ -390,8 +392,10 @@ void Sector::updateRoughArea() { if(leaf->isDegenerate()) continue; - d->roughArea += (leaf->poly().aaBox().maxX - leaf->poly().aaBox().minX) * - (leaf->poly().aaBox().maxY - leaf->poly().aaBox().minY); + AABoxd const &leafAABox = leaf->poly().firstFace()->aaBox(); + + d->roughArea += (leafAABox.maxX - leafAABox.minX) * + (leafAABox.maxY - leafAABox.minY); } } diff --git a/doomsday/client/src/render/rend_bias.cpp b/doomsday/client/src/render/rend_bias.cpp index 9f84ad2c69..235948f120 100644 --- a/doomsday/client/src/render/rend_bias.cpp +++ b/doomsday/client/src/render/rend_bias.cpp @@ -795,7 +795,7 @@ void SB_RendPoly(struct ColorRawf_s *rcolors, BiasSurface *bsuf, BspLeaf const *bspLeaf = mapElement->castTo(); DENG_ASSERT(!bspLeaf->isDegenerate()); - Vector3d point(bspLeaf->poly().center(), + Vector3d point(bspLeaf->poly().firstFace()->center(), bspLeaf->sector().plane(elmIdx).height()); updateAffected2(bsuf, rvertices, numVertices, point, surfaceNormal); diff --git a/doomsday/client/src/render/rend_clip.cpp b/doomsday/client/src/render/rend_clip.cpp index 73cfd68d48..e2571e39a7 100644 --- a/doomsday/client/src/render/rend_clip.cpp +++ b/doomsday/client/src/render/rend_clip.cpp @@ -967,7 +967,7 @@ int C_CheckBspLeaf(BspLeaf &leaf) // Do we need to resize the angle list buffer? de::Polygon const &poly = leaf.poly(); - HEdge const *firstHEdge = poly.firstHEdge(); + HEdge const *firstHEdge = poly.firstFace()->hedge(); int const hedgeCount = poly.hedgeCount(); if(hedgeCount > anglistSize) diff --git a/doomsday/client/src/render/rend_fakeradio.cpp b/doomsday/client/src/render/rend_fakeradio.cpp index 3a15990e8e..da62bf5e2e 100644 --- a/doomsday/client/src/render/rend_fakeradio.cpp +++ b/doomsday/client/src/render/rend_fakeradio.cpp @@ -1337,8 +1337,9 @@ void Rend_RadioBspLeafEdges(BspLeaf &bspLeaf) // Any need to continue? if(shadowDark < .0001f) return; - Vector3f eyeToSurface(vOrigin[VX] - bspLeaf.poly().center().x, - vOrigin[VZ] - bspLeaf.poly().center().y); + Face const &face = *bspLeaf.poly().firstFace(); + Vector3f eyeToSurface(vOrigin[VX] - face.center().x, + vOrigin[VZ] - face.center().y); // Do we need to enlarge the size of the doPlanes array? if(sector.planeCount() > doPlaneSize) diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index 06b7a6037c..b83bde840e 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -1584,22 +1584,23 @@ static uint buildLeafPlaneGeometry(BspLeaf const &leaf, ClockDirection direction DENG_ASSERT(!leaf.isDegenerate()); DENG_ASSERT(verts != 0); - de::Polygon const &poly = leaf.poly(); - HEdge *firstHEdge = poly.firstHEdge(); - int const hedgeCount = poly.hedgeCount(); + Face const &face = *leaf.poly().firstFace(); + int const hedgeCount = leaf.poly().hedgeCount(); HEdge *fanBase = leaf.fanBase(); - uint totalVerts = hedgeCount + (!fanBase? 2 : 0); *verts = R_AllocRendVertices(totalVerts); + uint totalVerts = hedgeCount + (!fanBase? 2 : 0); + + *verts = R_AllocRendVertices(totalVerts); int n = 0; if(!fanBase) { - V3f_Set((*verts)[n].pos, poly.center().x, poly.center().y, height); + V3f_Set((*verts)[n].pos, face.center().x, face.center().y, height); n++; } // Add the vertices for each hedge. - HEdge *baseNode = fanBase? fanBase : firstHEdge; + HEdge *baseNode = fanBase? fanBase : face.hedge(); HEdge *node = baseNode; do { @@ -1610,7 +1611,7 @@ static uint buildLeafPlaneGeometry(BspLeaf const &leaf, ClockDirection direction // The last vertex is always equal to the first. if(!fanBase) { - V3f_Set((*verts)[n].pos, firstHEdge->origin().x, firstHEdge->origin().y, height); + V3f_Set((*verts)[n].pos, face.hedge()->origin().x, face.hedge()->origin().y, height); } if(vertsSize) *vertsSize = totalVerts; @@ -1622,9 +1623,11 @@ static void writeLeafPlane(Plane &plane) BspLeaf *leaf = currentBspLeaf; DENG_ASSERT(!isNullLeaf(leaf)); + Face const &face = *leaf->poly().firstFace(); + Surface const &surface = plane.surface(); - Vector3f eyeToSurface(vOrigin[VX] - leaf->poly().center().x, - vOrigin[VZ] - leaf->poly().center().y, + Vector3f eyeToSurface(vOrigin[VX] - face.center().x, + vOrigin[VZ] - face.center().y, vOrigin[VY] - plane.visHeight()); // Skip planes facing away from the viewer. @@ -1651,18 +1654,18 @@ static void writeLeafPlane(Plane &plane) // Add the Y offset to orient the Y flipped material. /// @todo fixme: What is this meant to do? -ds if(plane.type() == Plane::Ceiling) - materialOrigin.y -= leaf->poly().aaBox().maxY - leaf->poly().aaBox().minY; + materialOrigin.y -= face.aaBox().maxY - face.aaBox().minY; materialOrigin.y = -materialOrigin.y; Vector2f materialScale((surface.flags() & DDSUF_MATERIAL_FLIPH)? -1 : 1, (surface.flags() & DDSUF_MATERIAL_FLIPV)? -1 : 1); // Set the texture origin, Y is flipped for the ceiling. - Vector3d texTL(leaf->poly().aaBox().minX, - leaf->poly().aaBox().arvec2[plane.type() == Plane::Floor? 1 : 0][VY], + Vector3d texTL(face.aaBox().minX, + face.aaBox().arvec2[plane.type() == Plane::Floor? 1 : 0][VY], plane.visHeight()); - Vector3d texBR(leaf->poly().aaBox().maxX, - leaf->poly().aaBox().arvec2[plane.type() == Plane::Floor? 0 : 1][VY], + Vector3d texBR(face.aaBox().maxX, + face.aaBox().arvec2[plane.type() == Plane::Floor? 0 : 1][VY], plane.visHeight()); rendworldpoly_params_t parm; zap(parm); @@ -2973,7 +2976,7 @@ static void Rend_DrawSurfaceVectors() foreach(Plane *plane, sector.planes()) { - origin = Vector3d(bspLeaf->poly().center(), plane->visHeight()); + origin = Vector3d(bspLeaf->poly().firstFace()->center(), plane->visHeight()); if(plane->type() != Plane::Middle && plane->surface().hasSkyMaskedMaterial()) origin.z = theMap->skyFix(plane->type() == Plane::Ceiling); diff --git a/doomsday/server/server.pro b/doomsday/server/server.pro index 2c0a1207ee..aa34e60108 100644 --- a/doomsday/server/server.pro +++ b/doomsday/server/server.pro @@ -199,6 +199,7 @@ DENG_HEADERS += \ $$SRC/include/map/bspnode.h \ $$SRC/include/map/dam_file.h \ $$SRC/include/map/entitydatabase.h \ + $$SRC/include/map/face.h \ $$SRC/include/map/gamemap.h \ $$SRC/include/map/generators.h \ $$SRC/include/map/hedge.h \ @@ -372,6 +373,7 @@ SOURCES += \ $$SRC/src/map/bspnode.cpp \ $$SRC/src/map/dam_file.cpp \ $$SRC/src/map/entitydatabase.cpp \ + $$SRC/src/map/face.cpp \ $$SRC/src/map/gamemap.cpp \ $$SRC/src/map/generators.cpp \ $$SRC/src/map/hedge.cpp \