From b1e63eb5a7596ff57b0e816b28bf248f0a6fe185 Mon Sep 17 00:00:00 2001 From: danij Date: Mon, 1 Dec 2014 17:38:50 +0000 Subject: [PATCH] Refactor|World|ConvexSubspace: Various ConvexSubspace API-cleanup refactorings --- .../client/include/world/convexsubspace.h | 237 +++++++++--------- doomsday/client/src/render/rend_fakeradio.cpp | 14 +- doomsday/client/src/render/rend_main.cpp | 130 ++++++---- doomsday/client/src/render/viewports.cpp | 58 +++-- doomsday/client/src/world/api_map.cpp | 25 +- doomsday/client/src/world/convexsubspace.cpp | 210 +++++++++------- doomsday/client/src/world/linesighttest.cpp | 38 +-- doomsday/client/src/world/sectorcluster.cpp | 21 +- 8 files changed, 415 insertions(+), 318 deletions(-) diff --git a/doomsday/client/include/world/convexsubspace.h b/doomsday/client/include/world/convexsubspace.h index a7d5ccde58..9a0448ba30 100644 --- a/doomsday/client/include/world/convexsubspace.h +++ b/doomsday/client/include/world/convexsubspace.h @@ -21,12 +21,10 @@ #ifndef DENG_WORLD_CONVEXSUBSPACE_H #define DENG_WORLD_CONVEXSUBSPACE_H -#include +#include #include #include - #include "Mesh" - #include "MapElement" #include "Line" #include "SectorCluster" @@ -48,21 +46,12 @@ class ConvexSubspace : public de::MapElement /// An invalid polygon was specified. @ingroup errors DENG2_ERROR(InvalidPolyError); + /// Required BspLeaf attribution is missing. @ingroup errors + DENG2_ERROR(MissingBspLeafError); + /// Required sector cluster attribution is missing. @ingroup errors DENG2_ERROR(MissingClusterError); - /// Linked-element lists/sets: - typedef QSet Meshes; - typedef QSet Polyobjs; - -#ifdef __CLIENT__ - typedef QSet Lumobjs; - typedef QSet ShadowLines; - - // Final audio environment characteristics. - typedef uint AudioEnvironmentFactors[NUM_REVERB_DATA]; -#endif - public: /** * Attempt to construct a ConvexSubspace from the Face geometry provided. @@ -73,18 +62,6 @@ class ConvexSubspace : public de::MapElement */ static ConvexSubspace *newFromConvexPoly(de::Face &poly, BspLeaf *bspLeaf = 0); - /** - * Returns the BspLeaf to which the subspace is assigned. - */ - BspLeaf &bspLeaf() const; - - void setBspLeaf(BspLeaf *newBspLeaf); - - /** - * Provides access to the attributed convex geometry (a polygon). - */ - de::Face &poly() const; - /** * Determines whether the specified @a point in the map coordinate space * lies inside the convex polygon geometry of the subspace on the XY plane. @@ -97,6 +74,11 @@ class ConvexSubspace : public de::MapElement */ bool contains(de::Vector2d const &point) const; + /** + * Provides access to the attributed convex geometry (a polygon). + */ + de::Face &poly() const; + /** * Assign an additional mesh geometry to the subspace. Such @em extra meshes * are used to represent geometry which would otherwise result in a @@ -108,69 +90,36 @@ class ConvexSubspace : public de::MapElement void assignExtraMesh(de::Mesh &mesh); /** - * Provides access to the set of 'extra' mesh geometries for the subspace. - * - * @see assignExtraMesh() - */ - Meshes const &extraMeshes() const; - - /** - * Remove the given @a polyobj from the set of those linked to the subspace. + * Iterate through the 'extra' meshes of the subspace. * - * @return @c true= @a polyobj was linked and subsequently removed. - */ - bool unlink(polyobj_s const &polyobj); - - /** - * Add the given @a polyobj to the set of those linked to the subspace. - * Ownership is unaffected. If the polyobj is already linked in this set - * then nothing will happen. - */ - void link(struct polyobj_s const &polyobj); - - /** - * Provides access to the set of polyobjs linked to the subspace. + * @param func Callback to make for each Mesh. */ - Polyobjs const &polyobjs() const; + de::LoopResult forAllExtraMeshes(std::function func) const; /** - * Convenient method of returning the total number of polyobjs linked to the - * subspace. + * Returns @c true iff a SectorCluster is attributed to the subspace. The + * only time a cluster might not be attributed is during initial map setup. */ - inline int polyobjCount() { return polyobjs().count(); } + bool hasCluster() const; /** - * Returns the vector described by the offset from the map coordinate space - * origin to the top most, left most point of the geometry of the subspace. + * Returns the SectorCluster attributed to the subspace. * - * @see aaBox() - */ - de::Vector2d const &worldGridOffset() const; - - /** - * Returns @c true iff a SectorCluster is attributed to the subspace. The - * only time a cluster might not be attributed is during initial map setup. + * @see hasCluster() */ - bool hasCluster() const; + SectorCluster &cluster() const; + SectorCluster *clusterPtr() const; /** * Change the sector cluster attributed to the subspace. * * @param newCluster New sector cluster to attribute to the subspace. - * Ownership is unaffected. Can be @c 0 (to clear the - * attribution). + * Ownership is unaffected. Use @c nullptr to clear. * * @see hasCluster(), cluster() */ void setCluster(SectorCluster *newCluster); - /** - * Returns the SectorCluster attributed to the subspace. - * - * @see hasCluster() - */ - SectorCluster &cluster() const; - /** * Convenient method returning Sector of the SectorCluster attributed to the * subspace. @@ -180,14 +129,10 @@ class ConvexSubspace : public de::MapElement inline Sector §or() const { return cluster().sector(); } /** - * Convenient method returning a pointer to the SectorCluster attributed to - * the subspace. If not attributed then @c 0 is returned. - * - * @see hasCluster(), cluster() + * Returns the BspLeaf to which the subspace is assigned. */ - inline SectorCluster *clusterPtr() const { - return hasCluster()? &cluster() : 0; - } + BspLeaf &bspLeaf() const; + void setBspLeaf(BspLeaf *newBspLeaf); /** * Returns the @em validCount of the subspace. Used by some legacy iteration @@ -202,23 +147,73 @@ class ConvexSubspace : public de::MapElement #ifdef __CLIENT__ /** - * Clear the list of fake radio shadow line sides for the subspace. + * Returns the vector described by the offset from the map coordinate space + * origin to the top most, left most point of the geometry of the subspace. + * + * @see aaBox() */ - void clearShadowLines(); + de::Vector2d const &worldGridOffset() const; /** - * Add the specified line @a side to the set of fake radio shadow lines for - * the subspace. If the line is already present in this set then nothing - * will happen. + * Returns a pointer to the face geometry half-edge which has been chosen + * for use as the base for a triangle fan GL primitive. May return @c 0 if + * no suitable base was determined. + */ + de::HEdge *fanBase() const; + + /** + * Returns the number of vertices needed for a triangle fan GL primitive. * - * @param side Map line side to add to the set. + * @note When first called after a face geometry is assigned a new 'base' + * half-edge for the triangle fan primitive will be determined. + * + * @see fanBase() */ - void addShadowLine(LineSide &side); + int fanVertexCount() const; /** - * Provides access to the set of fake radio shadow lines for the subspace. + * Returns the frame number of the last time mobj sprite projection was + * performed for the subspace. */ - ShadowLines const &shadowLines() const; + int lastSpriteProjectFrame() const; + void setLastSpriteProjectFrame(int newFrameNumber); + +public: // Audio Environment (reverb) --------------------------------------------- + /** + * Audio environment characteristics. + */ + struct AudioEnvironmentData + { + // Final reverb factors. + typedef uint ReverbFactors[NUM_REVERB_DATA]; + ReverbFactors reverb; + + AudioEnvironmentData() { de::zap(reverb); } + }; + + /** + * Recalculate the environmental audio characteristics (reverb) of the subspace. + */ + bool updateAudioEnvironment(); + + /** + * Provides access to the final environmental audio environment characteristics + * of the subspace, for efficient accumulation. + */ + AudioEnvironmentData const &audioEnvironmentData() const; + +public: // Luminous objects ------------------------------------------------------- + /** + * Returns the total number of Lumobjs linked to the subspace. + */ + int lumobjCount() const; + + /** + * Iterate through the Lumobjs of the subspace. + * + * @param func Callback to make for each Lumobj. + */ + de::LoopResult forAllLumobjs(std::function func) const; /** * Clear all lumobj links for the subspace. @@ -241,65 +236,75 @@ class ConvexSubspace : public de::MapElement * * @param lumobj Lumobj to link. * - * @see lumobjs(), unlink() + * @see unlink() */ void link(Lumobj &lumobj); +#endif // __CLIENT__ +public: // Poly objects ----------------------------------------------------------- /** - * Provides access to the set of lumobjs linked to the subspace. - * - * @see linkLumobj(), clearLumobjs() + * Returns the total number of Polyobjs linked to the subspace. */ - Lumobjs const &lumobjs() const; + int polyobjCount() const; /** - * Returns the frame number of the last time mobj sprite projection was - * performed for the subspace. + * Iterate through the Polyobjs of the subspace. + * + * @param func Callback to make for each Polyobj. */ - int lastSpriteProjectFrame() const; + de::LoopResult forAllPolyobjs(std::function func) const; /** - * Change the frame number of the last time mobj sprite projection was - * performed for the subspace. + * Remove the given @a polyobj from the set of those linked to the subspace. * - * @param newFrame New frame number. + * @return @c true= @a polyobj was linked and subsequently removed. */ - void setLastSpriteProjectFrame(int newFrame); + bool unlink(struct polyobj_s const &polyobj); /** - * Returns a pointer to the face geometry half-edge which has been chosen - * for use as the base for a triangle fan GL primitive. May return @c 0 if - * no suitable base was determined. + * Add the given @a polyobj to the set of those linked to the subspace. + * Ownership is unaffected. If the polyobj is already linked in this set + * then nothing will happen. */ - de::HEdge *fanBase() const; + void link(struct polyobj_s const &polyobj); +#ifdef __CLIENT__ +public: // Shadowing-lines (fakeradio) -------------------------------------------- /** - * Returns the number of vertices needed for a triangle fan GL primitive. - * - * @note When first called after a face geometry is assigned a new 'base' - * half-edge for the triangle fan primitive will be determined. - * - * @see fanBase() + * Returns the total number of shadow line sides linked in the subspace. */ - int numFanVertices() const; + int shadowLineCount() const; /** - * Recalculate the environmental audio characteristics (reverb) of the subspace. + * Clear the list of fake radio shadow line sides for the subspace. */ - bool updateReverb(); + void clearShadowLines(); /** - * Provides access to the final environmental audio characteristics (reverb) - * of the subspace, for efficient accumulation. + * Add the specified line @a side to the set of fake radio shadow lines for + * the subspace. If the line is already present in this set then nothing + * will happen. + * + * @param side Map line side to add to the set. */ - AudioEnvironmentFactors const &reverb() const; + void addShadowLine(LineSide &side); + /** + * Iterate through the set of fake radio shadow lines for the subspace. + * + * @param func Callback to make for each LineSide. + */ + de::LoopResult forAllShadowLines(std::function func) const; #endif // __CLIENT__ private: - ConvexSubspace(de::Face &convexPolygon, BspLeaf *bspLeaf = 0); + ConvexSubspace(de::Face &convexPolygon, BspLeaf *bspLeaf = nullptr); DENG2_PRIVATE(d) }; +#ifdef __CLIENT__ +typedef ConvexSubspace::AudioEnvironmentData ConvexSubspaceAudioEnvironmentData; +#endif + #endif // DENG_WORLD_CONVEXSUBSPACE_H diff --git a/doomsday/client/src/render/rend_fakeradio.cpp b/doomsday/client/src/render/rend_fakeradio.cpp index e8bdc4321f..3f22d495ae 100644 --- a/doomsday/client/src/render/rend_fakeradio.cpp +++ b/doomsday/client/src/render/rend_fakeradio.cpp @@ -1308,8 +1308,7 @@ void Rend_RadioSubspaceEdges(ConvexSubspace const &subspace) if(!rendFakeRadio) return; if(levelFullBright) return; - ConvexSubspace::ShadowLines const &shadowLines = subspace.shadowLines(); - if(shadowLines.isEmpty()) return; + if(!subspace.shadowLineCount()) return; SectorCluster &cluster = subspace.cluster(); float sectorlight = cluster.lightSourceIntensity(); @@ -1326,13 +1325,13 @@ void Rend_RadioSubspaceEdges(ConvexSubspace const &subspace) // We need to check all the shadow lines linked to this subspace for // the purpose of fakeradio shadowing. - foreach(LineSide *side, shadowLines) + subspace.forAllShadowLines([&cluster, &shadowDark, &eyeToSurface] (LineSide &side) { // Already rendered during the current frame? We only want to // render each shadow once per frame. - if(side->shadowVisCount() != R_FrameCount()) + if(side.shadowVisCount() != R_FrameCount()) { - side->setShadowVisCount(R_FrameCount()); + side.setShadowVisCount(R_FrameCount()); for(int pln = 0; pln < cluster.visPlaneCount(); ++pln) { @@ -1340,11 +1339,12 @@ void Rend_RadioSubspaceEdges(ConvexSubspace const &subspace) if(Vector3f(eyeToSurface, Rend_EyeOrigin().y - plane.heightSmoothed()) .dot(plane.surface().normal()) >= 0) { - writeShadowSection(pln, *side, shadowDark); + writeShadowSection(pln, side, shadowDark); } } } - } + return LoopContinue; + }); } #ifdef DENG_DEBUG diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index 9c5153af9c..e09bdcc0ac 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -2602,17 +2602,23 @@ static void writeSubspaceWallSections() writeAllWallSections(hedge); } while((hedge = &hedge->next()) != base); - for(Mesh *mesh : curSubspace->extraMeshes()) - for(HEdge *hedge : mesh->hedges()) + curSubspace->forAllExtraMeshes([] (Mesh &mesh) { - writeAllWallSections(hedge); - } + for(HEdge *hedge : mesh.hedges()) + { + writeAllWallSections(hedge); + } + return LoopContinue; + }); - for(Polyobj *po : curSubspace->polyobjs()) - for(HEdge *hedge : po->mesh().hedges()) + curSubspace->forAllPolyobjs([] (Polyobj &pob) { - writeAllWallSections(hedge); - } + for(HEdge *hedge : pob.mesh().hedges()) + { + writeAllWallSections(hedge); + } + return LoopContinue; + }); } static void writeSubspacePlanes() @@ -2649,17 +2655,23 @@ static void markSubspaceFrontFacingWalls() markFrontFacingWalls(hedge); } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, curSubspace->extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) + curSubspace->forAllExtraMeshes([] (Mesh &mesh) { - markFrontFacingWalls(hedge); - } + for(HEdge *hedge : mesh.hedges()) + { + markFrontFacingWalls(hedge); + } + return LoopContinue; + }); - foreach(Polyobj *po, curSubspace->polyobjs()) - foreach(HEdge *hedge, po->mesh().hedges()) + curSubspace->forAllPolyobjs([] (Polyobj &pob) { - markFrontFacingWalls(hedge); - } + for(HEdge *hedge : pob.mesh().hedges()) + { + markFrontFacingWalls(hedge); + } + return LoopContinue; + }); } static inline bool canOccludeEdgeBetweenPlanes(Plane &frontPlane, Plane const &backPlane) @@ -2749,10 +2761,12 @@ static void occludeSubspace(bool frontFacing) static void clipSubspaceLumobjs() { - foreach(Lumobj *lum, curSubspace->lumobjs()) + DENG2_ASSERT(curSubspace); + curSubspace->forAllLumobjs([] (Lumobj &lob) { - R_ViewerClipLumobj(lum); - } + R_ViewerClipLumobj(&lob); + return LoopContinue; + }); } /** @@ -2763,12 +2777,14 @@ static void clipSubspaceLumobjs() static void clipSubspaceLumobjsBySight() { // Any work to do? + DENG2_ASSERT(curSubspace); if(!curSubspace->polyobjCount()) return; - foreach(Lumobj *lum, curSubspace->lumobjs()) + curSubspace->forAllLumobjs([] (Lumobj &lob) { - R_ViewerClipLumobjBySight(lum, curSubspace); - } + R_ViewerClipLumobjBySight(&lob, curSubspace); + return LoopContinue; + }); } /// If not front facing this is no-op. @@ -2796,17 +2812,23 @@ static void clipSubspaceFrontFacingWalls() clipFrontFacingWalls(hedge); } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, curSubspace->extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) + curSubspace->forAllExtraMeshes([] (Mesh &mesh) { - clipFrontFacingWalls(hedge); - } + for(HEdge *hedge : mesh.hedges()) + { + clipFrontFacingWalls(hedge); + } + return LoopContinue; + }); - foreach(Polyobj *po, curSubspace->polyobjs()) - foreach(HEdge *hedge, po->mesh().hedges()) + curSubspace->forAllPolyobjs([] (Polyobj &pob) { - clipFrontFacingWalls(hedge); - } + for(HEdge *hedge : pob.mesh().hedges()) + { + clipFrontFacingWalls(hedge); + } + return LoopContinue; + }); } static int projectSpriteWorker(mobj_t &mo, void * /*context*/) @@ -4616,17 +4638,23 @@ static void drawSurfaceTangentVectors(SectorCluster &cluster) drawTangentVectorsForWallSections(hedge); } while((hedge = &hedge->next()) != base); - for(Mesh *mesh : subspace->extraMeshes()) - for(HEdge *hedge : mesh->hedges()) + subspace->forAllExtraMeshes([] (Mesh &mesh) { - drawTangentVectorsForWallSections(hedge); - } + for(HEdge *hedge : mesh.hedges()) + { + drawTangentVectorsForWallSections(hedge); + } + return LoopContinue; + }); - for(Polyobj *polyobj : subspace->polyobjs()) - for(HEdge *hedge : polyobj->mesh().hedges()) + subspace->forAllPolyobjs([] (Polyobj &pob) { - drawTangentVectorsForWallSections(hedge); - } + for(HEdge *hedge : pob.mesh().hedges()) + { + drawTangentVectorsForWallSections(hedge); + } + return LoopContinue; + }); } int const planeCount = cluster.sector().planeCount(); @@ -4982,19 +5010,25 @@ static void drawSubspaceVertexs(ConvexSubspace &sub, drawVertexVisual_params_t & } while((hedge = &hedge->next()) != base); - for(Mesh *mesh : sub.extraMeshes()) - for(HEdge *hedge : mesh->hedges()) + sub.forAllExtraMeshes([&min, &max, &parms] (Mesh &mesh) { - drawVertexVisual(hedge->vertex(), min, max, parms); - drawVertexVisual(hedge->twin().vertex(), min, max, parms); - } + for(HEdge *hedge : mesh.hedges()) + { + drawVertexVisual(hedge->vertex(), min, max, parms); + drawVertexVisual(hedge->twin().vertex(), min, max, parms); + } + return LoopContinue; + }); - for(Polyobj *polyobj : sub.polyobjs()) - for(Line *line : polyobj->lines()) + sub.forAllPolyobjs([&min, &max, &parms] (Polyobj &pob) { - drawVertexVisual(line->from(), min, max, parms); - drawVertexVisual(line->to(), min, max, parms); - } + for(Line *line : pob.lines()) + { + drawVertexVisual(line->from(), min, max, parms); + drawVertexVisual(line->to(), min, max, parms); + } + return LoopContinue; + }); } /** diff --git a/doomsday/client/src/render/viewports.cpp b/doomsday/client/src/render/viewports.cpp index a78f37fa6a..bff306b5bf 100644 --- a/doomsday/client/src/render/viewports.cpp +++ b/doomsday/client/src/render/viewports.cpp @@ -1238,6 +1238,13 @@ bool R_ViewerLumobjIsHidden(int idx) return false; } +static void markLumobjClipped(Lumobj const &lob, bool yes = true) +{ + int const index = lob.indexInMap(); + DENG2_ASSERT(index >= 0 && index < lob.map().lumobjCount()); + luminousClipped[index] = yes? 1 : 0; +} + /// Used to sort lumobjs by distance from viewpoint. static int lumobjSorter(void const *e1, void const *e2) { @@ -1324,7 +1331,7 @@ void R_ViewerClipLumobj(Lumobj *lum) if(luminousClipped[lumIdx] > 1) return; - luminousClipped[lumIdx] = 0; + markLumobjClipped(*lum, false); /// @todo Determine the exact centerpoint of the light in addLuminous! Vector3d origin = lum->origin(); @@ -1334,57 +1341,58 @@ void R_ViewerClipLumobj(Lumobj *lum) { if(!C_IsPointVisible(origin)) { - luminousClipped[lumIdx] = 1; // Won't have a halo. + markLumobjClipped(*lum); // Won't have a halo. } } else { - luminousClipped[lumIdx] = 1; + markLumobjClipped(*lum); Vector3d const eye = Rend_EyeOrigin().xzy(); - if(LineSightTest(eye, origin, -1, 1, LS_PASSLEFT | LS_PASSOVER | LS_PASSUNDER) .trace(lum->map().bspTree())) { - luminousClipped[lumIdx] = 0; // Will have a halo. + markLumobjClipped(*lum, false); // Will have a halo. } } } -void R_ViewerClipLumobjBySight(Lumobj *lum, ConvexSubspace *subspace) +void R_ViewerClipLumobjBySight(Lumobj *lob, ConvexSubspace *subspace) { - if(!lum || !subspace) return; + if(!lob || !subspace) return; // Already clipped? - int lumIdx = lum->indexInMap(); - if(luminousClipped[lumIdx]) + if(luminousClipped[lob->indexInMap()]) return; // We need to figure out if any of the polyobj's segments lies // between the viewpoint and the lumobj. Vector3d const eye = Rend_EyeOrigin().xzy(); - for(Polyobj *po : subspace->polyobjs()) - for(HEdge *hedge : po->mesh().hedges()) + subspace->forAllPolyobjs([&lob, &eye] (Polyobj &pob) { - // Is this on the back of a one-sided line? - if(!hedge->hasMapElement()) - continue; - - // Ignore half-edges facing the wrong way. - if(hedge->mapElementAs().isFrontFacing()) + for(HEdge *hedge : pob.mesh().hedges()) { - coord_t eyeV1[2] = { eye.x, eye.y }; - coord_t lumOriginV1[2] = { lum->origin().x, lum->origin().y }; - coord_t fromV1[2] = { hedge->origin().x, hedge->origin().y }; - coord_t toV1[2] = { hedge->twin().origin().x, hedge->twin().origin().y }; - if(V2d_Intercept2(lumOriginV1, eyeV1, fromV1, toV1, 0, 0, 0)) + // Is this on the back of a one-sided line? + if(!hedge->hasMapElement()) + continue; + + // Ignore half-edges facing the wrong way. + if(hedge->mapElementAs().isFrontFacing()) { - luminousClipped[lumIdx] = 1; - break; + coord_t eyeV1[2] = { eye.x, eye.y }; + coord_t lumOriginV1[2] = { lob->origin().x, lob->origin().y }; + coord_t fromV1[2] = { hedge->origin().x, hedge->origin().y }; + coord_t toV1[2] = { hedge->twin().origin().x, hedge->twin().origin().y }; + if(V2d_Intercept2(lumOriginV1, eyeV1, fromV1, toV1, 0, 0, 0)) + { + markLumobjClipped(*lob); + break; + } } } - } + return LoopContinue; + }); } D_CMD(ViewGrid) diff --git a/doomsday/client/src/world/api_map.cpp b/doomsday/client/src/world/api_map.cpp index 9943638795..b84530fd48 100644 --- a/doomsday/client/src/world/api_map.cpp +++ b/doomsday/client/src/world/api_map.cpp @@ -396,7 +396,7 @@ int P_Iteratep(void *elPtr, uint prop, int (*callback) (void *p, void *ctx), voi switch(prop) { case DMU_LINE: - foreach(LineSide *side, sector.sides()) + for(LineSide *side : sector.sides()) { if(int result = callback(&side->line(), context)) return result; @@ -404,7 +404,7 @@ int P_Iteratep(void *elPtr, uint prop, int (*callback) (void *p, void *ctx), voi return false; // Continue iteration case DMU_PLANE: - foreach(Plane *plane, sector.planes()) + for(Plane *plane : sector.planes()) { if(int result = callback(plane, context)) return result; @@ -432,17 +432,20 @@ int P_Iteratep(void *elPtr, uint prop, int (*callback) (void *p, void *ctx), voi } } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, subspace.extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) + LoopResult result = subspace.forAllExtraMeshes([&callback, &context] (Mesh &mesh) { - // Is this on the back of a one-sided line? - if(!hedge->hasMapElement()) - continue; + for(HEdge *hedge : mesh.hedges()) + { + // Is this on the back of a one-sided line? + if(!hedge->hasMapElement()) + continue; - if(int result = callback(&hedge->mapElement().as().line(), context)) - return result; - } - return false; /* Continue iteration */ } + if(int result = callback(&hedge->mapElement().as().line(), context)) + return LoopResult( result ); + } + return LoopResult(); // continue + }); + return result; } default: throw Error("P_Iteratep", QString("Property %1 unknown/not vector").arg(DMU_Str(prop))); diff --git a/doomsday/client/src/world/convexsubspace.cpp b/doomsday/client/src/world/convexsubspace.cpp index 831dc838b8..c98c6eee4c 100644 --- a/doomsday/client/src/world/convexsubspace.cpp +++ b/doomsday/client/src/world/convexsubspace.cpp @@ -18,15 +18,16 @@ * 02110-1301 USA */ -#include "de_base.h" #include "world/convexsubspace.h" + +#include +#include +#include #include "BspLeaf" #include "Face" #include "Polyobj" #include "SectorCluster" #include "Surface" -#include -#include using namespace de; @@ -44,41 +45,37 @@ ddouble triangleArea(Vector2d const &v1, Vector2d const &v2, Vector2d const &v3) DENG2_PIMPL(ConvexSubspace) { - Face &poly; ///< Convex polygon geometry (not owned). - Meshes extraMeshes; ///< Additional meshes (owned). - Polyobjs polyobjs; ///< Linked polyobjs (if any, not owned). - SectorCluster *cluster; ///< Attributed cluster (if any, not owned). - int validCount; ///< Used to prevent repeated processing. - Vector2d worldGridOffset; ///< For aligning the materials to the map space grid. - BspLeaf *bspLeaf; + Face *poly = nullptr; ///< Convex polygon geometry (not owned). -#ifdef __CLIENT__ - int addSpriteCount; ///< Frame number of last R_AddSprites. - Lumobjs lumobjs; ///< Linked lumobjs (not owned). - ShadowLines shadowLines; ///< Linked map lines for fake radio shadowing. + typedef QSet Meshes; + Meshes extraMeshes; ///< Additional meshes (owned). - HEdge *fanBase; ///< Trifan base Half-edge (otherwise the center point is used). - bool needUpdateFanBase; ///< @c true= need to rechoose a fan base half-edge. - AudioEnvironmentFactors reverb; ///< Cached characteristics. -#endif + typedef QSet Polyobjs; + Polyobjs polyobjs; ///< Linked polyobjs (not owned). + + SectorCluster *cluster = nullptr; ///< Attributed cluster (if any, not owned). + BspLeaf *bspLeaf = nullptr; ///< Attributed BSP leaf (if any, not owned). - Instance(Public *i, Face &poly) - : Base(i) - , poly (poly) - , cluster (0) - , validCount (0) - , bspLeaf (0) -#ifdef __CLIENT__ - , addSpriteCount (0) - , fanBase (0) - , needUpdateFanBase(true) -#endif - { #ifdef __CLIENT__ - de::zap(reverb); + Vector2d worldGridOffset; ///< For aligning the materials to the map space grid. + + typedef QSet Lumobjs; + Lumobjs lumobjs; ///< Linked lumobjs (not owned). + + typedef QSet ShadowLines; + ShadowLines shadowLines; ///< Linked map lines for fake radio shadowing. + + HEdge *fanBase = nullptr; ///< Trifan base Half-edge (otherwise the center point is used). + bool needUpdateFanBase = true; ///< @c true= need to rechoose a fan base half-edge. + + AudioEnvironmentData aenv; ///< Cached audio environment characteristics. + + int lastSpriteProjectFrame = 0; ///< Frame number of last R_AddSprites. #endif - } + int validCount = 0; ///< Used to prevent repeated processing. + + Instance(Public *i) : Base(i) {} ~Instance() { qDeleteAll(extraMeshes); } #ifdef __CLIENT__ @@ -105,11 +102,11 @@ DENG2_PIMPL(ConvexSubspace) { #define MIN_TRIANGLE_EPSILON (0.1) ///< Area - HEdge *firstNode = poly.hedge(); + HEdge *firstNode = self.poly().hedge(); fanBase = firstNode; - if(poly.hedgeCount() > 3) + if(self.poly().hedgeCount() > 3) { // Splines with higher vertex counts demand checking. Vertex const *base, *a, *b; @@ -162,13 +159,15 @@ DENG2_PIMPL(ConvexSubspace) ConvexSubspace::ConvexSubspace(Face &convexPolygon, BspLeaf *bspLeaf) : MapElement(DMU_SUBSPACE) - , d(new Instance(this, convexPolygon)) + , d(new Instance(this)) { + d->poly = &convexPolygon; +#ifdef __CLIENT__ // Determine the world grid offset. - d->worldGridOffset = Vector2d(fmod(d->poly.aaBox().minX, 64), - fmod(d->poly.aaBox().maxY, 64)); - - d->poly.setMapElement(this); + d->worldGridOffset = Vector2d(fmod(poly().aaBox().minX, 64), + fmod(poly().aaBox().maxY, 64)); +#endif + poly().setMapElement(this); setBspLeaf(bspLeaf); } @@ -176,7 +175,7 @@ ConvexSubspace *ConvexSubspace::newFromConvexPoly(de::Face &poly, BspLeaf *bspLe { if(!poly.isConvex()) { - /// @throw InvalidPolyError Attempted to attribute a non-convex polygon. + /// @throw InvalidPolyError Attempted to attribute a non-convex polygon. throw InvalidPolyError("ConvexSubspace::newFromConvexPoly", "Source is non-convex"); } return new ConvexSubspace(poly, bspLeaf); @@ -184,8 +183,9 @@ ConvexSubspace *ConvexSubspace::newFromConvexPoly(de::Face &poly, BspLeaf *bspLe BspLeaf &ConvexSubspace::bspLeaf() const { - DENG2_ASSERT(d->bspLeaf != 0); - return *d->bspLeaf; + if(d->bspLeaf) return *d->bspLeaf; + /// @throw MissingBspLeafError Attempted with no BspLeaf attributed. + throw MissingBspLeafError("ConvexSubspace::bspLeaf", "No BSP leaf is attributed"); } void ConvexSubspace::setBspLeaf(BspLeaf *newBspLeaf) @@ -195,7 +195,8 @@ void ConvexSubspace::setBspLeaf(BspLeaf *newBspLeaf) Face &ConvexSubspace::poly() const { - return d->poly; + DENG2_ASSERT(d->poly); + return *d->poly; } bool ConvexSubspace::contains(Vector2d const &point) const @@ -231,38 +232,46 @@ void ConvexSubspace::assignExtraMesh(Mesh &newMesh) LOG_DEBUG("Assigned extra mesh to subspace %p") << this; // Attribute all faces to "this" subspace. - foreach(Face *face, newMesh.faces()) + for(Face *face : newMesh.faces()) { face->setMapElement(this); } } } -ConvexSubspace::Meshes const &ConvexSubspace::extraMeshes() const +LoopResult ConvexSubspace::forAllExtraMeshes(std::function func) const { - return d->extraMeshes; + for(Mesh *mesh : d->extraMeshes) + { + if(auto result = func(*mesh)) return result; + } + return LoopContinue; } -void ConvexSubspace::link(Polyobj const &polyobj) +int ConvexSubspace::polyobjCount() const { - d->polyobjs.insert(const_cast(&polyobj)); + return d->polyobjs.count(); } -bool ConvexSubspace::unlink(polyobj_s const &polyobj) +LoopResult ConvexSubspace::forAllPolyobjs(std::function func) const { - int sizeBefore = d->polyobjs.size(); - d->polyobjs.remove(const_cast(&polyobj)); - return d->polyobjs.size() != sizeBefore; + for(Polyobj *pob : d->polyobjs) + { + if(auto result = func(*pob)) return result; + } + return LoopContinue; } -ConvexSubspace::Polyobjs const &ConvexSubspace::polyobjs() const +void ConvexSubspace::link(Polyobj const &polyobj) { - return d->polyobjs; + d->polyobjs.insert(const_cast(&polyobj)); } -Vector2d const &ConvexSubspace::worldGridOffset() const +bool ConvexSubspace::unlink(Polyobj const &polyobj) { - return d->worldGridOffset; + int sizeBefore = d->polyobjs.size(); + d->polyobjs.remove(const_cast(&polyobj)); + return d->polyobjs.size() != sizeBefore; } bool ConvexSubspace::hasCluster() const @@ -272,14 +281,16 @@ bool ConvexSubspace::hasCluster() const SectorCluster &ConvexSubspace::cluster() const { - if(d->cluster) - { - return *d->cluster; - } - /// @throw MissingClusterError Attempted with no sector cluster attributed. + if(d->cluster) return *d->cluster; + /// @throw MissingClusterError Attempted with no sector cluster attributed. throw MissingClusterError("ConvexSubspace::cluster", "No sector cluster is attributed"); } +SectorCluster *ConvexSubspace::clusterPtr() const +{ + return hasCluster()? &cluster() : nullptr; +} + void ConvexSubspace::setCluster(SectorCluster *newCluster) { d->cluster = newCluster; @@ -296,6 +307,16 @@ void ConvexSubspace::setValidCount(int newValidCount) } #ifdef __CLIENT__ +Vector2d const &ConvexSubspace::worldGridOffset() const +{ + return d->worldGridOffset; +} + +int ConvexSubspace::shadowLineCount() const +{ + return d->shadowLines.count(); +} + void ConvexSubspace::clearShadowLines() { d->shadowLines.clear(); @@ -306,9 +327,27 @@ void ConvexSubspace::addShadowLine(LineSide &side) d->shadowLines.insert(&side); } -ConvexSubspace::ShadowLines const &ConvexSubspace::shadowLines() const +LoopResult ConvexSubspace::forAllShadowLines(std::function func) const +{ + for(LineSide *side : d->shadowLines) + { + if(auto result = func(*side)) return result; + } + return LoopContinue; +} + +int ConvexSubspace::lumobjCount() const +{ + return d->lumobjs.count(); +} + +LoopResult ConvexSubspace::forAllLumobjs(std::function func) const { - return d->shadowLines; + for(Lumobj *lob : d->lumobjs) + { + if(auto result = func(*lob)) return result; + } + return LoopContinue; } void ConvexSubspace::unlinkAllLumobjs() @@ -326,19 +365,14 @@ void ConvexSubspace::link(Lumobj &lumobj) d->lumobjs.insert(&lumobj); } -ConvexSubspace::Lumobjs const &ConvexSubspace::lumobjs() const -{ - return d->lumobjs; -} - int ConvexSubspace::lastSpriteProjectFrame() const { - return d->addSpriteCount; + return d->lastSpriteProjectFrame; } -void ConvexSubspace::setLastSpriteProjectFrame(int newFrameCount) +void ConvexSubspace::setLastSpriteProjectFrame(int newFrameNumber) { - d->addSpriteCount = newFrameCount; + d->lastSpriteProjectFrame = newFrameNumber; } HEdge *ConvexSubspace::fanBase() const @@ -350,10 +384,10 @@ HEdge *ConvexSubspace::fanBase() const return d->fanBase; } -int ConvexSubspace::numFanVertices() const +int ConvexSubspace::fanVertexCount() const { // Are we to use one of the half-edge vertexes as the fan base? - return d->poly.hedgeCount() + (fanBase()? 0 : 2); + return poly().hedgeCount() + (fanBase()? 0 : 2); } static void accumReverbForWallSections(HEdge const *hedge, @@ -370,19 +404,21 @@ static void accumReverbForWallSections(HEdge const *hedge, Material &material = seg.lineSide().middle().material(); AudioEnvironmentId env = material.audioEnvironment(); if(!(env >= 0 && env < NUM_AUDIO_ENVIRONMENTS)) + { env = AE_WOOD; // Assume it's wood if unknown. + } total += seg.length(); envSpaceAccum[env] += seg.length(); } -bool ConvexSubspace::updateReverb() +bool ConvexSubspace::updateAudioEnvironment() { if(!hasCluster()) { - d->reverb[SRD_SPACE] = d->reverb[SRD_VOLUME] = - d->reverb[SRD_DECAY] = d->reverb[SRD_DAMPING] = 0; + d->aenv.reverb[SRD_SPACE] = d->aenv.reverb[SRD_VOLUME] = + d->aenv.reverb[SRD_DECAY] = d->aenv.reverb[SRD_DAMPING] = 0; return false; } @@ -390,22 +426,22 @@ bool ConvexSubspace::updateReverb() de::zap(envSpaceAccum); // Space is the rough volume of the BSP leaf (bounding box). - AABoxd const &aaBox = d->poly.aaBox(); - d->reverb[SRD_SPACE] = int(cluster().ceiling().height() - cluster().floor().height()) + AABoxd const &aaBox = poly().aaBox(); + d->aenv.reverb[SRD_SPACE] = int(cluster().ceiling().height() - cluster().floor().height()) * (aaBox.maxX - aaBox.minX) * (aaBox.maxY - aaBox.minY); // The other reverb properties can be found out by taking a look at the // materials of all surfaces in the BSP leaf. float total = 0; - HEdge *base = d->poly.hedge(); + HEdge *base = poly().hedge(); HEdge *hedge = base; do { accumReverbForWallSections(hedge, envSpaceAccum, total); } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, d->extraMeshes) - foreach(HEdge *hedge, mesh->hedges()) + for(Mesh *mesh : d->extraMeshes) + for(HEdge *hedge : mesh->hedges()) { accumReverbForWallSections(hedge, envSpaceAccum, total); } @@ -413,7 +449,7 @@ bool ConvexSubspace::updateReverb() if(!total) { // Huh? - d->reverb[SRD_VOLUME] = d->reverb[SRD_DECAY] = d->reverb[SRD_DAMPING] = 0; + d->aenv.reverb[SRD_VOLUME] = d->aenv.reverb[SRD_DECAY] = d->aenv.reverb[SRD_DAMPING] = 0; return false; } @@ -437,16 +473,16 @@ bool ConvexSubspace::updateReverb() // High frequency damping. accum[SRD_DAMPING] += envSpaceAccum[i] * envInfo.dampingMul; } - d->reverb[SRD_VOLUME] = de::min(accum[SRD_VOLUME], 255); - d->reverb[SRD_DECAY] = de::min(accum[SRD_DECAY], 255); - d->reverb[SRD_DAMPING] = de::min(accum[SRD_DAMPING], 255); + d->aenv.reverb[SRD_VOLUME] = de::min(accum[SRD_VOLUME], 255); + d->aenv.reverb[SRD_DECAY] = de::min(accum[SRD_DECAY], 255); + d->aenv.reverb[SRD_DAMPING] = de::min(accum[SRD_DAMPING], 255); return true; } -ConvexSubspace::AudioEnvironmentFactors const &ConvexSubspace::reverb() const +ConvexSubspace::AudioEnvironmentData const &ConvexSubspace::audioEnvironmentData() const { - return d->reverb; + return d->aenv; } #endif // __CLIENT__ diff --git a/doomsday/client/src/world/linesighttest.cpp b/doomsday/client/src/world/linesighttest.cpp index 2a4162a8e3..4ad4c0eac2 100644 --- a/doomsday/client/src/world/linesighttest.cpp +++ b/doomsday/client/src/world/linesighttest.cpp @@ -239,14 +239,18 @@ DENG2_PIMPL(LineSightTest) ConvexSubspace const &subspace = bspLeaf.subspace(); // Check polyobj lines. - for(Polyobj *po : subspace.polyobjs()) - for(Line *line : po->lines()) + LoopResult blocked = subspace.forAllPolyobjs([this] (Polyobj &pob) { - if(!crossLine(line->front())) - return false; // Stop traversal. - } + for(Line *line : pob.lines()) + { + if(!crossLine(line->front())) + return LoopAbort; + } + return LoopContinue; + }); + if(blocked) return false; - // Check the lines for the edges of the subspace geometry. + // Check lines for the edges of the subspace geometry. HEdge *base = subspace.poly().hedge(); HEdge *hedge = base; do @@ -258,18 +262,22 @@ DENG2_PIMPL(LineSightTest) } } while((hedge = &hedge->next()) != base); - for(Mesh *mesh : subspace.extraMeshes()) - for(HEdge *hedge : mesh->hedges()) + // Check lines for the extra meshes. + blocked = subspace.forAllExtraMeshes([this] (Mesh &mesh) { - // Is this on the back of a one-sided line? - if(!hedge->hasMapElement()) - continue; + for(HEdge *hedge : mesh.hedges()) + { + // Is this on the back of a one-sided line? + if(!hedge->hasMapElement()) + continue; - if(!crossLine(hedge->mapElementAs().lineSide())) - return false; - } + if(!crossLine(hedge->mapElementAs().lineSide())) + return LoopAbort; + } + return LoopContinue; + }); - return true; // Continue traversal. + return !blocked; } /** diff --git a/doomsday/client/src/world/sectorcluster.cpp b/doomsday/client/src/world/sectorcluster.cpp index 5e144ffda5..1da4ce3126 100644 --- a/doomsday/client/src/world/sectorcluster.cpp +++ b/doomsday/client/src/world/sectorcluster.cpp @@ -734,7 +734,7 @@ DENG2_PIMPL(SectorCluster) if(!ddMapSetup && useBias) { // Inform bias surfaces of changed geometry. - foreach(ConvexSubspace *subspace, subspaces) + for(ConvexSubspace *subspace : subspaces) { if(Shard *shard = self.findShard(*subspace, plane.indexInSector())) { @@ -748,11 +748,14 @@ DENG2_PIMPL(SectorCluster) updateBiasForWallSectionsAfterGeometryMove(hedge); } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, subspace->extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) + subspace->forAllExtraMeshes([this] (Mesh &mesh) { - updateBiasForWallSectionsAfterGeometryMove(hedge); - } + for(HEdge *hedge : mesh.hedges()) + { + updateBiasForWallSectionsAfterGeometryMove(hedge); + } + return LoopContinue; + }); } } @@ -879,11 +882,11 @@ DENG2_PIMPL(SectorCluster) reverb[SRD_SPACE] = reverb[SRD_VOLUME] = reverb[SRD_DECAY] = reverb[SRD_DAMPING] = 0; - foreach(ConvexSubspace *subspace, reverbSubspaces) + for(ConvexSubspace *subspace : reverbSubspaces) { - if(subspace->updateReverb()) + if(subspace->updateAudioEnvironment()) { - ConvexSubspace::AudioEnvironmentFactors const &subReverb = subspace->reverb(); + auto const &subReverb = subspace->audioEnvironmentData().reverb; reverb[SRD_SPACE] += subReverb[SRD_SPACE]; @@ -1209,7 +1212,7 @@ static int countIlluminationPoints(MapElement &mapElement, int group) case DMU_SUBSPACE: { ConvexSubspace &subspace = mapElement.as(); DENG2_ASSERT(group >= 0 && group < subspace.sector().planeCount()); // sanity check - return subspace.numFanVertices(); } + return subspace.fanVertexCount(); } case DMU_SEGMENT: DENG2_ASSERT(group >= 0 && group <= LineSide::Top); // sanity check