diff --git a/doomsday/api/dd_share.h b/doomsday/api/dd_share.h index 3f6311af6e..eb6c0092b4 100644 --- a/doomsday/api/dd_share.h +++ b/doomsday/api/dd_share.h @@ -702,7 +702,7 @@ enum { MX, MY, MZ }; #define DD_BASE_POLYOBJ_ELEMENTS() \ DD_BASE_DDMOBJ_ELEMENTS() \ \ - BspLeaf *bspLeaf; /* bspLeaf in which this resides */ \ + BspLeaf *_bspLeaf; /* bspLeaf in which this resides */ \ int _indexInMap; \ int tag; /* Reference tag. */ \ int validCount; \ diff --git a/doomsday/client/include/map/line.h b/doomsday/client/include/map/line.h index 2d9f4cc5dc..25f86c5f2e 100644 --- a/doomsday/client/include/map/line.h +++ b/doomsday/client/include/map/line.h @@ -31,6 +31,7 @@ #include #include "MapElement" +#include "Polyobj" #include "Surface" #include "Vertex" @@ -63,6 +64,9 @@ class Line : public de::MapElement /// Required sector attribution is missing. @ingroup errors DENG2_ERROR(MissingSectorError); + /// Required polyobj attribution is missing. @ingroup errors + DENG2_ERROR(MissingPolyobjError); + /// The given side section identifier is invalid. @ingroup errors DENG2_ERROR(InvalidSectionIdError); @@ -740,16 +744,27 @@ class Line : public de::MapElement } /** - * Returns @c true iff the line is part of some Polyobj. + * Returns @c true iff the line defines a section of some Polyobj. + */ + bool definesPolyobj() const; + + /** + * Returns the Polyobj for which the line is a defining section. + * + * @see definesPolyobj() */ - bool isFromPolyobj() const; + Polyobj &polyobj() const; /** - * Mark the line as being owned by/is part of some Polyobj. + * Change the attributed polyobj which owns the line. * - * @param set @c true to set, @c false to clear. + * @param newOwner New polyobj to attribute as the to the line. Can be + * @c 0, to clear the attribution. (Note that the polyobj + * may also represent this relationship, so the relevant + * method(s) of Polyobj will also need to be called to + * complete the job of removing this relationship.) */ - void markPolyobjOwned(bool set = true); + void setPolyobj(Polyobj *newOwner); /** * Returns @c true iff the line resulted in the creation of a BSP window diff --git a/doomsday/client/include/map/polyobj.h b/doomsday/client/include/map/polyobj.h index 4eeb89df7f..8ecc4692a6 100644 --- a/doomsday/client/include/map/polyobj.h +++ b/doomsday/client/include/map/polyobj.h @@ -41,6 +41,9 @@ class Vertex; typedef struct polyobj_s { public: + /// The polyobj is not presently linked in the BSP. @ingroup errors + DENG2_ERROR(NotLinkedError); + typedef QList Lines; typedef QList Vertexes; @@ -55,6 +58,37 @@ typedef struct polyobj_s /// @note: Does nothing about the user data section. ~polyobj_s(); + /** + * Returns @c true if the polyobj is presently linked in the owning GameMap. + */ + bool isLinked(); + + /** + * (Re)link the polyobj in the owning GameMap. Ownership is not affected. + * To be called @em after rotation and/or translation to re-link the polyobj + * and thereby complete the process. + * + * Linking only occurs if the polyobj is not presently linked (subsequent calls + * are ignored). + */ + void link(); + + /** + * Unlink the polyobj in the owning GameMap. To be called @em before attempting + * to rotate and/or translate the polyobj to initiate the process. + * + * Unlinking only occurs if the polyobj is presently linked (subsequent calls + * are ignored). + */ + void unlink(); + + /** + * Returns the BSP leaf in which the polyobj is presently linked. + * + * @see isLinked(); + */ + BspLeaf &bspLeaf() const; + /** * Returns the sound emitter for the polyobj. */ diff --git a/doomsday/client/src/edit_map.cpp b/doomsday/client/src/edit_map.cpp index b8e1ceaa75..23a52250a5 100644 --- a/doomsday/client/src/edit_map.cpp +++ b/doomsday/client/src/edit_map.cpp @@ -1121,7 +1121,7 @@ int MPE_PolyobjCreate(int *lines, int lineCount, int tag, int sequenceType, if(lines[i] < 0 || lines[i] >= editMap.lines.count()) return -1; Line *line = editMap.lines[lines[i]]; - if(line->isFromPolyobj()) return -1; + if(line->definesPolyobj()) return -1; } Polyobj *po = editMap.createPolyobj(Vector2d(originX, originY)); @@ -1133,7 +1133,7 @@ int MPE_PolyobjCreate(int *lines, int lineCount, int tag, int sequenceType, Line *line = editMap.lines[lines[i]]; // This line belongs to a polyobj. - line->markPolyobjOwned(); + line->setPolyobj(po); static_cast(po->_lines)->append(line); } diff --git a/doomsday/client/src/map/bsp/partitioner.cpp b/doomsday/client/src/map/bsp/partitioner.cpp index e217d39c54..22de94d18c 100644 --- a/doomsday/client/src/map/bsp/partitioner.cpp +++ b/doomsday/client/src/map/bsp/partitioner.cpp @@ -265,7 +265,7 @@ DENG2_PIMPL(Partitioner) bool lineMightHaveWindowEffect(Line const &line) { - if(line.isFromPolyobj()) return false; + if(line.definesPolyobj()) return false; if(line.hasFrontSector() && line.hasBackSector()) return false; if(!line.hasFrontSector()) return false; //if(line.hasZeroLength() || line._buildData.overlap) return false; @@ -375,7 +375,7 @@ DENG2_PIMPL(Partitioner) foreach(Line *line, map->lines()) { // Polyobj lines are completely ignored. - if(line->isFromPolyobj()) continue; + if(line->definesPolyobj()) continue; LineSegment *front = 0; coord_t angle = 0; diff --git a/doomsday/client/src/map/gamemap.cpp b/doomsday/client/src/map/gamemap.cpp index c54dd3916b..4b57508602 100644 --- a/doomsday/client/src/map/gamemap.cpp +++ b/doomsday/client/src/map/gamemap.cpp @@ -829,35 +829,13 @@ void GameMap::initPolyobjs() foreach(Polyobj *po, _polyobjs) { - // Find the center point of the polyobj. - Vector2d avg; - foreach(Line *line, po->lines()) - { - avg += line->fromOrigin(); - } - avg /= po->lineCount(); - - // Given the center point determine in which BSP leaf the polyobj resides. - if(BspLeaf *bspLeaf = bspLeafAtPoint(avg)) - { - if(bspLeaf->hasPolyobj()) - { - LOG_WARNING("Multiple polyobjs in a single BSP leaf\n" - " (BSP leaf %i, sector %i). Previous polyobj overridden.") - << bspLeaf->indexInMap() - << bspLeaf->sector().indexInMap(); - } - bspLeaf->setFirstPolyobj(po); - po->bspLeaf = bspLeaf; - } - /// @todo Is this still necessary? -ds /// (This data is updated automatically when moving/rotating). po->updateAABox(); po->updateSurfaceTangents(); - P_PolyobjUnlink(po); - P_PolyobjLink(po); + po->unlink(); + po->link(); } } @@ -1055,7 +1033,7 @@ void GameMap::linkLine(Line &line) DENG_ASSERT(lineBlockmap != 0); // Lines of Polyobjs don't get into the blockmap (presently...). - if(line.isFromPolyobj()) return; + if(line.definesPolyobj()) return; vec2d_t origin; V2d_Copy(origin, Blockmap_Origin(lineBlockmap)); vec2d_t cellSize; V2d_Copy(cellSize, Blockmap_CellSize(lineBlockmap)); diff --git a/doomsday/client/src/map/line.cpp b/doomsday/client/src/map/line.cpp index 4d8c9736dd..ee862d6739 100644 --- a/doomsday/client/src/map/line.cpp +++ b/doomsday/client/src/map/line.cpp @@ -26,6 +26,7 @@ #include #include "m_misc.h" + #include "Sector" #include "Vertex" @@ -490,8 +491,8 @@ DENG2_PIMPL(Line) Side front; Side back; - /// @c true= the line is owned by some Polyobj. - bool polyobjOwned; + /// The polyobj which this line defines a section of, if any. + Polyobj *polyobj; /// Used by legacy algorithms to prevent repeated processing. int validCount; @@ -511,7 +512,7 @@ DENG2_PIMPL(Line) length(direction.length()), front(*i, frontSector), back(*i, backSector), - polyobjOwned(false), + polyobj(0), validCount(0) { std::memset(mapped, 0, sizeof(mapped)); @@ -559,14 +560,24 @@ bool Line::isBspWindow() const return _bspWindowSector != 0; } -bool Line::isFromPolyobj() const +bool Line::definesPolyobj() const +{ + return d->polyobj != 0; +} + +Polyobj &Line::polyobj() const { - return d->polyobjOwned; + if(d->polyobj) + { + return *d->polyobj; + } + /// @throw Line::MissingPolyobjError Attempted with no polyobj attributed. + throw Line::MissingPolyobjError("Line::polyobj", "No polyobj is attributed"); } -void Line::markPolyobjOwned(bool set) +void Line::setPolyobj(Polyobj *newOwner) { - d->polyobjOwned = set; + d->polyobj = newOwner; } Line::Side &Line::side(int back) diff --git a/doomsday/client/src/map/p_dmu.cpp b/doomsday/client/src/map/p_dmu.cpp index 8067501d1d..1dd2d51384 100644 --- a/doomsday/client/src/map/p_dmu.cpp +++ b/doomsday/client/src/map/p_dmu.cpp @@ -2245,16 +2245,14 @@ DENG_EXTERN_C void P_SetPolyobjCallback(void (*func) (struct mobj_s *, void *, v DENG_EXTERN_C void P_PolyobjUnlink(Polyobj *po) { if(!po) return; - /// @todo Do not assume polyobj is from the CURRENT map. - theMap->unlinkPolyobj(*po); + po->unlink(); } #undef P_PolyobjLink DENG_EXTERN_C void P_PolyobjLink(Polyobj *po) { if(!po) return; - /// @todo Do not assume polyobj is from the CURRENT map. - theMap->linkPolyobj(*po); + po->link(); } #undef P_PolyobjByID diff --git a/doomsday/client/src/map/polyobj.cpp b/doomsday/client/src/map/polyobj.cpp index a8f8a869f0..85e75336a5 100644 --- a/doomsday/client/src/map/polyobj.cpp +++ b/doomsday/client/src/map/polyobj.cpp @@ -26,9 +26,11 @@ #include "de_base.h" #include "map/p_maptypes.h" -#include "render/r_main.h" // validCount +#include "map/gamemap.h" #include "HEdge" +#include "render/r_main.h" // validCount + #include "map/polyobj.h" using namespace de; @@ -75,7 +77,7 @@ polyobj_s::polyobj_s(de::Vector2d const &origin_) { origin[VX] = origin_.x; origin[VY] = origin_.y; - bspLeaf = 0; + _bspLeaf = 0; tag = 0; validCount = 0; dest[0] = dest[1] = 0; @@ -109,6 +111,62 @@ void Polyobj::setCollisionCallback(void (*func) (mobj_t *mobj, void *line, void collisionCallback = func; } +bool Polyobj::isLinked() +{ + return _bspLeaf != 0; +} + +void Polyobj::unlink() +{ + if(_bspLeaf) + { + /// @todo Do not assume polyobj is from the CURRENT map. + theMap->unlinkPolyobj(*this); + _bspLeaf = 0; + } +} + +void Polyobj::link() +{ + if(!_bspLeaf) + { + theMap->linkPolyobj(*this); + + // Find the center point of the polyobj. + Vector2d avg; + foreach(Line *line, lines()) + { + avg += line->fromOrigin(); + } + avg /= lineCount(); + + // Given the center point determine in which BSP leaf the polyobj resides. + /// @todo Do not assume polyobj is from the CURRENT map. + if(BspLeaf *bspLeaf = theMap->bspLeafAtPoint(avg)) + { + if(bspLeaf->hasPolyobj()) + { + LOG_WARNING("Multiple polyobjs in a single BSP leaf\n" + " (BSP leaf %i, sector %i). Previous polyobj overridden.") + << bspLeaf->indexInMap() + << bspLeaf->sector().indexInMap(); + } + bspLeaf->setFirstPolyobj(this); + _bspLeaf = bspLeaf; + } + } +} + +BspLeaf &Polyobj::bspLeaf() const +{ + if(_bspLeaf) + { + return *_bspLeaf; + } + /// @throw Polyobj::NotLinkedError Attempted while the polyobj is not linked to the BSP. + throw Polyobj::NotLinkedError("Polyobj::bspLeaf", "Polyobj is not presently linked in the BSP"); +} + ddmobj_base_t &Polyobj::soundEmitter() { return *reinterpret_cast(this); @@ -261,7 +319,7 @@ bool Polyobj::move(Vector2d const &delta) LOG_AS("Polyobj::move"); //LOG_DEBUG("Applying delta %s to [%p]") << delta.asText() << this; - P_PolyobjUnlink(this); + unlink(); { VertexCoords::iterator prevCoordsIt = static_cast(_prevPts)->begin(); foreach(Vertex *vertex, uniqueVertexes()) @@ -285,14 +343,14 @@ bool Polyobj::move(Vector2d const &delta) updateAABox(); } - P_PolyobjLink(this); + link(); // With translation applied now determine if we collided with anything. if(mobjIsBlockingPolyobj(*this)) { //LOG_DEBUG("Blocked by mobj, undoing..."); - P_PolyobjUnlink(this); + unlink(); { VertexCoords::const_iterator prevCoordsIt = static_cast(_prevPts)->constBegin(); foreach(Vertex *vertex, uniqueVertexes()) @@ -311,7 +369,7 @@ bool Polyobj::move(Vector2d const &delta) updateAABox(); } - P_PolyobjLink(this); + link(); return false; } @@ -344,7 +402,7 @@ bool Polyobj::rotate(angle_t delta) //LOG_DEBUG("Applying delta %u (%f) to [%p]") // << delta << (delta / float( ANGLE_MAX ) * 360) << this; - P_PolyobjUnlink(this); + unlink(); { uint fineAngle = (angle + delta) >> ANGLETOFINESHIFT; @@ -375,14 +433,14 @@ bool Polyobj::rotate(angle_t delta) updateAABox(); angle += delta; } - P_PolyobjLink(this); + link(); // With rotation applied now determine if we collided with anything. if(mobjIsBlockingPolyobj(*this)) { //LOG_DEBUG("Blocked by mobj, undoing..."); - P_PolyobjUnlink(this); + unlink(); { VertexCoords::const_iterator prevCoordsIt = static_cast(_prevPts)->constBegin(); foreach(Vertex *vertex, uniqueVertexes()) @@ -402,7 +460,7 @@ bool Polyobj::rotate(angle_t delta) updateAABox(); angle -= delta; } - P_PolyobjLink(this); + link(); return false; } diff --git a/doomsday/client/src/render/r_fakeradio.cpp b/doomsday/client/src/render/r_fakeradio.cpp index d0ea87f071..10a5dc27ce 100644 --- a/doomsday/client/src/render/r_fakeradio.cpp +++ b/doomsday/client/src/render/r_fakeradio.cpp @@ -41,7 +41,7 @@ static zblockset_t *shadowLinksBlockSet; bool Rend_RadioLineCastsShadow(Line const &line) { - if(line.isFromPolyobj()) return false; + if(line.definesPolyobj()) return false; if(line.isSelfReferencing()) return false; // Lines with no other neighbor do not qualify for shadowing. diff --git a/doomsday/client/src/render/rend_fakeradio.cpp b/doomsday/client/src/render/rend_fakeradio.cpp index 20ed5cc029..e7acc776cf 100644 --- a/doomsday/client/src/render/rend_fakeradio.cpp +++ b/doomsday/client/src/render/rend_fakeradio.cpp @@ -1257,7 +1257,7 @@ static void writeShadowSection2(ShadowEdge const &leftEdge, ShadowEdge const &ri static void writeShadowSection(int planeIndex, Line::Side &side, float shadowDark) { DENG_ASSERT(side.hasSections()); - DENG_ASSERT(!side.line().isFromPolyobj()); + DENG_ASSERT(!side.line().definesPolyobj()); if(!(shadowDark > .0001)) return; diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index faaadf2935..53e879c167 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -1303,7 +1303,7 @@ static float wallSectionEdgeLightLevelDelta(Line::Side &side, int section, int e // Is delta smoothing disabled? if(!rendLightWallAngleSmooth) return delta; // ...always for polyobj lines (no owner rings). - if(side.line().isFromPolyobj()) return delta; + if(side.line().definesPolyobj()) return delta; /** * Smoothing is enabled, so find the neighbor side for the specified edge @@ -1636,7 +1636,7 @@ static bool writeWallSection(SectionEdge const &leftEdge, SectionEdge const &rig // Render FakeRadio for this section? if((flags & WSF_ADD_RADIO) && !(parm.flags & RPF_SKYMASK) && !(parm.glowing > 0) && currentSectorLightLevel > 0 && - !line.isFromPolyobj()) + !line.definesPolyobj()) { Rend_RadioUpdateForLineSide(side); @@ -2922,7 +2922,7 @@ void Rend_RenderSurfaceVectors() Vector3f origin; foreach(HEdge *hedge, theMap->hedges()) { - if(!hedge->hasLineSide() || hedge->line().isFromPolyobj()) + if(!hedge->hasLineSide() || hedge->line().definesPolyobj()) continue; if(!hedge->hasBspLeaf() || !hedge->bspLeaf().hasSector()) @@ -2998,7 +2998,7 @@ void Rend_RenderSurfaceVectors() foreach(Polyobj *polyobj, theMap->polyobjs()) { - Sector const §or = polyobj->bspLeaf->sector(); + Sector const §or = polyobj->bspLeaf().sector(); float zPos = sector.floor().height() + (sector.ceiling().height() - sector.floor().height())/2; foreach(Line *line, polyobj->lines()) @@ -3204,8 +3204,8 @@ static int drawVertex1(Line *li, void *context) if(alpha > 0) { - coord_t bottom = po->bspLeaf->sector().floor().visHeight(); - coord_t top = po->bspLeaf->sector().ceiling().visHeight(); + coord_t bottom = po->bspLeaf().sector().floor().visHeight(); + coord_t top = po->bspLeaf().sector().ceiling().visHeight(); if(devVertexBars) drawVertexBar(vtx, bottom, top, MIN_OF(alpha, .15f)); @@ -3224,7 +3224,7 @@ static int drawVertex1(Line *li, void *context) pos[VX] = vtx->origin().x; pos[VY] = vtx->origin().y; - pos[VZ] = po->bspLeaf->sector().floor().visHeight(); + pos[VZ] = po->bspLeaf().sector().floor().visHeight(); dist3D = V3d_Distance(pos, eye); @@ -3279,7 +3279,7 @@ void Rend_Vertexes() if(!own) continue; // Ignore polyobj vertexes. - if(own->line().isFromPolyobj()) continue; + if(own->line().definesPolyobj()) continue; float alpha = 1 - M_ApproxDistance(vOrigin[VX] - vertex->origin().x, vOrigin[VZ] - vertex->origin().y) / MAX_VERTEX_POINT_DIST; @@ -3309,7 +3309,7 @@ void Rend_Vertexes() if(!own) continue; // Ignore polyobj vertexes. - if(own->line().isFromPolyobj()) continue; + if(own->line().definesPolyobj()) continue; coord_t dist = M_ApproxDistance(vOrigin[VX] - vertex->origin().x, vOrigin[VZ] - vertex->origin().y); @@ -3340,7 +3340,7 @@ void Rend_Vertexes() if(!own) continue; // Ignore polyobj vertexes. - if(own->line().isFromPolyobj()) continue; + if(own->line().definesPolyobj()) continue; pos[VX] = vertex->origin().x; pos[VY] = vertex->origin().y; @@ -3803,7 +3803,7 @@ static void Rend_RenderBoundingBoxes() { foreach(Polyobj const *polyobj, theMap->polyobjs()) { - Sector const &sec = polyobj->bspLeaf->sector(); + Sector const &sec = polyobj->bspLeaf().sector(); coord_t width = (polyobj->aaBox.maxX - polyobj->aaBox.minX)/2; coord_t length = (polyobj->aaBox.maxY - polyobj->aaBox.minY)/2; coord_t height = (sec.ceiling().height() - sec.floor().height())/2;