From cd70c77aedaee295f239044def1717eda137d122 Mon Sep 17 00:00:00 2001 From: danij Date: Sat, 22 Nov 2014 07:58:25 +0000 Subject: [PATCH] Refactor|Map|World: Implement important map element/object iterations with C++11 lambdas Things are now looking significantly cleaner, and should also perform a little better in an optimized Release build. There are further iteration mechanisms which would benefit from using a lambda based approach. However, we've now reached the end of the "unstable" development phase for 1.15, so these will have to wait. --- doomsday/api/api_map.h | 19 + doomsday/client/include/mesh.h | 13 +- .../client/include/render/surfacedecorator.h | 4 +- doomsday/client/include/world/blockmap.h | 36 +- doomsday/client/include/world/line.h | 118 +-- doomsday/client/include/world/map.h | 752 +++++++------- doomsday/client/include/world/plane.h | 33 +- doomsday/client/include/world/surface.h | 66 +- doomsday/client/include/world/worldsystem.h | 12 +- doomsday/client/src/client/cl_sound.cpp | 20 +- doomsday/client/src/client/cl_world.cpp | 31 +- doomsday/client/src/dd_main.cpp | 11 +- doomsday/client/src/edit_bias.cpp | 50 +- doomsday/client/src/mesh.cpp | 79 +- doomsday/client/src/render/biastracker.cpp | 2 +- doomsday/client/src/render/blockmapvisual.cpp | 97 +- doomsday/client/src/render/r_fakeradio.cpp | 98 +- doomsday/client/src/render/r_things.cpp | 10 +- doomsday/client/src/render/rend_fakeradio.cpp | 32 +- doomsday/client/src/render/rend_main.cpp | 217 ++-- .../client/src/render/surfacedecorator.cpp | 45 +- doomsday/client/src/render/viewports.cpp | 13 +- .../client/src/resource/resourcesystem.cpp | 37 +- doomsday/client/src/ui/inputsystem.cpp | 14 +- doomsday/client/src/world/api_map.cpp | 174 +++- doomsday/client/src/world/api_mapedit.cpp | 10 +- doomsday/client/src/world/blockmap.cpp | 65 +- doomsday/client/src/world/contactspreader.cpp | 13 +- doomsday/client/src/world/generator.cpp | 147 +-- doomsday/client/src/world/interceptor.cpp | 57 +- doomsday/client/src/world/line.cpp | 199 ++-- doomsday/client/src/world/linesighttest.cpp | 8 +- doomsday/client/src/world/map.cpp | 971 ++++++++---------- doomsday/client/src/world/plane.cpp | 103 +- doomsday/client/src/world/sector.cpp | 24 +- doomsday/client/src/world/sectorcluster.cpp | 103 +- doomsday/client/src/world/surface.cpp | 186 ++-- doomsday/client/src/world/worldsystem.cpp | 91 +- doomsday/server/src/server/sv_pool.cpp | 132 +-- doomsday/server/src/serversystem.cpp | 102 +- doomsday/server/src/shelluser.cpp | 23 +- 41 files changed, 2143 insertions(+), 2074 deletions(-) diff --git a/doomsday/api/api_map.h b/doomsday/api/api_map.h index 345350dfe3..f007230437 100644 --- a/doomsday/api/api_map.h +++ b/doomsday/api/api_map.h @@ -315,6 +315,13 @@ DENG_API_TYPEDEF(Map) struct mobj_s *(*MO_CreateXYZ)(thinkfunc_t function, coord_t x, coord_t y, coord_t z, angle_t angle, coord_t radius, coord_t height, int ddflags); void (*MO_Destroy)(struct mobj_s *mobj); struct mobj_s *(*MO_ById)(int id); + + /** + * @note validCount should be incremented before calling this to begin a + * new logical traversal. Otherwise Mobjs marked with a validCount equal + * to this will be skipped over (can be used to avoid processing a mobj + * multiple times during a complex and/or non-linear traversal. + */ int (*MO_BoxIterator)(AABoxd const *box, int (*callback) (struct mobj_s *, void *), void *context); /** @@ -412,6 +419,12 @@ DENG_API_TYPEDEF(Map) */ struct polyobj_s *(*PO_ByTag)(int tag); + /** + * @note validCount should be incremented before calling this to begin a + * new logical traversal. Otherwise Polyobjs marked with a validCount equal + * to this will be skipped over (can be used to avoid processing a polyobj + * multiple times during a complex and/or non-linear traversal. + */ int (*PO_BoxIterator)(AABoxd const *box, int (*callback) (struct polyobj_s *, void *), void *context); /** @@ -419,6 +432,12 @@ DENG_API_TYPEDEF(Map) */ void (*PO_SetCallback)(void (*func)(struct mobj_s *, void *, void *)); + /** + * @note validCount should be incremented before calling this to begin a + * new logical traversal. Otherwise Polyobjs marked with a validCount equal + * to this will be skipped over (can be used to avoid processing a polyobj + * multiple times during a complex and/or non-linear traversal. + */ int (*SS_BoxIterator)(AABoxd const *box, int (*callback) (ConvexSubspace *, void *), void *context); // Traversers diff --git a/doomsday/client/include/mesh.h b/doomsday/client/include/mesh.h index 0643405e8d..2e0f43ae89 100644 --- a/doomsday/client/include/mesh.h +++ b/doomsday/client/include/mesh.h @@ -1,6 +1,6 @@ -/** @file mesh.h Mesh Geometry Data Structure. +/** @file mesh.h Mesh Geometry Data Structure. * - * @authors Copyright © 2008-2013 Daniel Swanson + * @authors Copyright © 2008-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -45,7 +45,7 @@ class HEdge; class Mesh { public: - typedef QList Vertexes; + typedef QList Vertexs; typedef QList Faces; typedef QList HEdges; @@ -113,6 +113,7 @@ class Mesh public: Mesh(); + ~Mesh(); /** * Clear the mesh destroying all geometry elements. @@ -155,7 +156,7 @@ class Mesh /** * Returns the total number of vertexes in the mesh. */ - inline int vertexCount() const { return vertexes().count(); } + inline int vertexCount() const { return vertexs().count(); } /** * Returns the total number of faces in the mesh. @@ -170,7 +171,7 @@ class Mesh /** * Returns @c true iff there are no vertexes in the mesh. */ - inline bool vertexesIsEmpty() const { return vertexes().isEmpty(); } + inline bool vertexsIsEmpty() const { return vertexs().isEmpty(); } /** * Returns @c true iff there are no faces in the mesh. @@ -185,7 +186,7 @@ class Mesh /** * Provides access to the set of all vertexes in the mesh. */ - Vertexes const &vertexes() const; + Vertexs const &vertexs() const; /** * Provides access to the set of all faces in the mesh. diff --git a/doomsday/client/include/render/surfacedecorator.h b/doomsday/client/include/render/surfacedecorator.h index 764da93ebf..abd6604a6d 100644 --- a/doomsday/client/include/render/surfacedecorator.h +++ b/doomsday/client/include/render/surfacedecorator.h @@ -1,7 +1,7 @@ -/** @file surfacedecorator.h World surface decorator. +/** @file surfacedecorator.h World surface decorator. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html diff --git a/doomsday/client/include/world/blockmap.h b/doomsday/client/include/world/blockmap.h index 68c3c91c1d..95fbf02e7c 100644 --- a/doomsday/client/include/world/blockmap.h +++ b/doomsday/client/include/world/blockmap.h @@ -1,7 +1,7 @@ -/** @file blockmap.h World map element blockmap. +/** @file blockmap.h World map element blockmap. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -21,16 +21,13 @@ #ifndef DENG_WORLD_BLOCKMAP_H #define DENG_WORLD_BLOCKMAP_H +#include #include - #include -#ifdef min -# undef min -#endif - -#ifdef max -# undef max +#ifdef WIN32 +# undef max +# undef min #endif namespace de { @@ -152,27 +149,28 @@ class Blockmap void unlinkAll(); /** - * Iterate over all elements in the specified @a cell. + * Iterate through all objects in the given @a cell. */ - int iterate(Cell const &cell, int (*callback) (void *elem, void *context), void *context = 0) const; + LoopResult forAllInCell(Cell const &cell, std::function func) const; /** - * Iterate over all elements in cells which intercept the specified map space - * @a region. + * Iterate through all objects in all cells which intercept the given map + * space, axis-aligned bounding @a box. */ - int iterate(AABoxd const ®ion, int (*callback) (void *elem, void *context), void *context = 0) const; + LoopResult forAllInBox(AABoxd const &box, std::function func) const; /** - * Iterate over all elements in cells which intercept the line specified by - * the two map space points @a from and @a to. Note that if an element is + * Iterate over all objects in cells which intercept the line specified by + * the two map space points @a from and @a to. Note that if an object is * processed/visited it does @em not mean that the line actually intercepts - * the element. Further testing between the line and the geometry of the map - * element is necessary if this is a requirement. + * the objects. Further testing between the line and the geometry of the map + * object is necessary if this is a requirement. * * @param from Map space point defining the origin of the line. * @param to Map space point defining the destination of the line. */ - int iterate(Vector2d const &from, Vector2d const &to, int (*callback) (void *elem, void *context), void *context = 0) const; + LoopResult forAllInPath(Vector2d const &from, Vector2d const &to, + std::function func) const; /** * Render a visual for this gridmap to assist in debugging (etc...). diff --git a/doomsday/client/include/world/line.h b/doomsday/client/include/world/line.h index f0e5ead5da..13ce6001e5 100644 --- a/doomsday/client/include/world/line.h +++ b/doomsday/client/include/world/line.h @@ -1,7 +1,7 @@ /** @file line.h World map line. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -21,19 +21,16 @@ #ifndef DENG_WORLD_LINE_H #define DENG_WORLD_LINE_H +#include +#include +#include +#include +#include #include "HEdge" - #include "MapElement" #include "Polyobj" #include "Vertex" -#include -#include -#include -#include -#include -#include - class LineOwner; class Sector; class Surface; @@ -134,9 +131,7 @@ class Line : public de::MapElement /** * Returns the line side owner of the segment. */ - inline Side &lineSide() { return parent().as(); } - - /// @copydoc lineSide() + inline Side &lineSide() { return parent().as(); } inline Side const &lineSide() const { return parent().as(); } /** @@ -145,9 +140,7 @@ class Line : public de::MapElement * * @see lineSide() */ - inline Line &line() { return lineSide().line(); } - - /// @copydoc line() + inline Line &line() { return lineSide().line(); } inline Line const &line() const { return lineSide().line(); } /** @@ -193,8 +186,6 @@ class Line : public de::MapElement DENG2_PRIVATE(d) }; - typedef QList Segments; - public: /** * Construct a new line side. @@ -208,9 +199,7 @@ class Line : public de::MapElement /** * Returns the Line owner of the side. */ - inline Line &line() { return parent().as(); } - - /// @copydoc line() + inline Line &line() { return parent().as(); } inline Line const &line() const { return parent().as(); } /** @@ -270,7 +259,7 @@ class Line : public de::MapElement * * @see vertex(), from() */ - inline Vertex &to() const { return vertex(To); } + inline Vertex &to () const { return vertex(To); } /** * Returns @c true iff Sections are defined for the side. @@ -291,9 +280,7 @@ class Line : public de::MapElement * * @param sectionId Identifier of the surface to return. */ - Surface &surface(int sectionId); - - /// @copydoc surface() + Surface &surface(int sectionId); Surface const &surface(int sectionId) const; /** @@ -301,9 +288,7 @@ class Line : public de::MapElement * * @see surface() */ - inline Surface &middle() { return surface(Middle); } - - /// @copydoc middle() + inline Surface &middle() { return surface(Middle); } inline Surface const &middle() const { return surface(Middle); } /** @@ -311,9 +296,7 @@ class Line : public de::MapElement * * @see surface() */ - inline Surface &bottom() { return surface(Bottom); } - - /// @copydoc bottom() + inline Surface &bottom() { return surface(Bottom); } inline Surface const &bottom() const { return surface(Bottom); } /** @@ -321,9 +304,7 @@ class Line : public de::MapElement * * @see surface() */ - inline Surface &top() { return surface(Top); } - - /// @copydoc top() + inline Surface &top() { return surface(Top); } inline Surface const &top() const { return surface(Top); } /** @@ -333,9 +314,7 @@ class Line : public de::MapElement * * @see Section::soundEmitter() */ - SoundEmitter &soundEmitter(int sectionId); - - /// @copydoc soundEmitter() + SoundEmitter &soundEmitter(int sectionId); SoundEmitter const &soundEmitter(int sectionId) const; /** @@ -343,9 +322,7 @@ class Line : public de::MapElement * * @see Section::soundEmitter() */ - inline SoundEmitter &middleSoundEmitter() { return soundEmitter(Middle); } - - /// @copydoc middleSoundEmitter() + inline SoundEmitter &middleSoundEmitter() { return soundEmitter(Middle); } inline SoundEmitter const &middleSoundEmitter() const { return soundEmitter(Middle); } /** @@ -353,9 +330,7 @@ class Line : public de::MapElement * * @see Section::soundEmitter() */ - inline SoundEmitter &bottomSoundEmitter() { return soundEmitter(Bottom); } - - /// @copydoc bottomSoundEmitter() + inline SoundEmitter &bottomSoundEmitter() { return soundEmitter(Bottom); } inline SoundEmitter const &bottomSoundEmitter() const { return soundEmitter(Bottom); } /** @@ -363,9 +338,7 @@ class Line : public de::MapElement * * @see Section::soundEmitter() */ - inline SoundEmitter &topSoundEmitter() { return soundEmitter(Top); } - - /// @copydoc topSoundEmitter() + inline SoundEmitter &topSoundEmitter() { return soundEmitter(Top); } inline SoundEmitter const &topSoundEmitter() const { return soundEmitter(Top); } /** @@ -391,7 +364,7 @@ class Line : public de::MapElement * Update the @em top sound emitter origin for the side. * @see updateSoundEmitterOrigin() */ - inline void updateTopSoundEmitterOrigin() { updateSoundEmitterOrigin(Top); } + inline void updateTopSoundEmitterOrigin() { updateSoundEmitterOrigin(Top); } /** * Update ALL sound emitter origins for the side. @@ -442,11 +415,6 @@ class Line : public de::MapElement */ Segment *addSegment(de::HEdge &hedge); - /** - * Provides access to the sorted segment list for the side. - */ - Segments const &segments() const; - /** * Convenient method of returning the half-edge of the left-most segment * on this side of the line; otherwise @c 0 (no segments exist). @@ -519,44 +487,38 @@ class Line : public de::MapElement public: /// @todo make private: /// Links to vertex line owner nodes: - LineOwner *_vo1; - LineOwner *_vo2; + LineOwner *_vo1 = nullptr; + LineOwner *_vo2 = nullptr; /// Sector of the map for which this line acts as a "One-way window". /// @todo Now unnecessary, refactor away -ds - Sector *_bspWindowSector; + Sector *_bspWindowSector = nullptr; public: Line(Vertex &from, Vertex &to, - int flags = 0, - Sector *frontSector = 0, - Sector *backSector = 0); + int flags = 0, + Sector *frontSector = nullptr, + Sector *backSector = nullptr); /** * Returns the specified logical side of the line. * * @param back If not @c 0 return the Back side; otherwise the Front side. */ - Side &side(int back); - - /// @copydoc side() + Side &side(int back); Side const &side(int back) const; /** * Returns the logical Front side of the line. */ - inline Side &front() { return side(Front); } - - /// @copydoc front() + inline Side &front() { return side(Front); } inline Side const &front() const { return side(Front); } /** * Returns the logical Back side of the line. */ - inline Side &back() { return side(Back); } - - /// @copydoc back() - inline Side const &back() const { return side(Back); } + inline Side &back() { return side(Back); } + inline Side const &back() const { return side(Back); } /** * Returns @c true iff Side::Sections are defined for the specified side @@ -574,7 +536,7 @@ class Line : public de::MapElement /** * Returns @c true iff Side::Sections are defined for the Back side of the line. */ - inline bool hasBackSections() const { return hasSections(Back); } + inline bool hasBackSections() const { return hasSections(Back); } /** * Returns @c true iff a sector is attributed to the specified side of the line. @@ -591,7 +553,7 @@ class Line : public de::MapElement /** * Returns @c true iff a sector is attributed to the Back side of the line. */ - inline bool hasBackSector() const { return hasSector(Back); } + inline bool hasBackSector() const { return hasSector(Back); } /** * Convenient accessor method for returning the sector attributed to the @@ -619,7 +581,7 @@ class Line : public de::MapElement /** * Returns the sector attributed to the Back side of the line. */ - inline Sector &backSector() const { return sector(Back); } + inline Sector &backSector() const { return sector(Back); } /** * Convenient accessor method for returning a pointer to the sector attributed @@ -631,7 +593,7 @@ class Line : public de::MapElement * Convenient accessor method for returning a pointer to the sector attributed * to the back side of the line. */ - inline Sector *backSectorPtr() const { return sectorPtr(Back); } + inline Sector *backSectorPtr() const { return sectorPtr(Back); } /** * Returns @c true iff the line is considered @em self-referencing. @@ -666,6 +628,11 @@ class Line : public de::MapElement */ inline Vertex &from() const { return vertex(From); } + /** + * Returns the To/End vertex for the line. + */ + inline Vertex &to() const { return vertex(To); } + /** * Convenient accessor method for returning the origin of the From/Start * vertex for the line. @@ -674,18 +641,13 @@ class Line : public de::MapElement */ inline de::Vector2d const &fromOrigin() const { return from().origin(); } - /** - * Returns the To/End vertex for the line. - */ - inline Vertex &to() const { return vertex(To); } - /** * Convenient accessor method for returning the origin of the To/End * vertex for the line. * * @see to() */ - inline de::Vector2d const &toOrigin() const { return to().origin(); } + inline de::Vector2d const &toOrigin() const { return to().origin(); } /** * Returns the point on the line which lies at the exact center of the @@ -774,7 +736,7 @@ class Line : public de::MapElement /** * @param offset Returns the position of the nearest point along the line [0..1]. */ - coord_t pointDistance(de::Vector2d const &point, coord_t *offset = 0) const; + coord_t pointDistance(de::Vector2d const &point, coord_t *offset = nullptr) const; /** * Where does the given @a point lie relative to the line? Note that the diff --git a/doomsday/client/include/world/map.h b/doomsday/client/include/world/map.h index f607f2ada5..133e2d9790 100644 --- a/doomsday/client/include/world/map.h +++ b/doomsday/client/include/world/map.h @@ -21,6 +21,15 @@ #ifndef DENG_WORLD_MAP_H #define DENG_WORLD_MAP_H +#include +#include +#include +#include +#include +#include +#include +#include + #include "Mesh" #include "BspNode" @@ -39,15 +48,8 @@ # include "Lumobj" #endif -#include -#include -#include -#include -#include -#include -#include - class MapDef; + class BspLeaf; class ConvexSubspace; class LineBlockmap; @@ -88,6 +90,12 @@ class Map /// Base error for runtime map editing errors. @ingroup errors DENG2_ERROR(EditError); + /// Required map element is missing. @ingroup errors + DENG2_ERROR(MissingElementError); + + /// Required map object is missing. @ingroup errors + DENG2_ERROR(MissingObjectError); + /// Required blockmap is missing. @ingroup errors DENG2_ERROR(MissingBlockmapError); @@ -101,7 +109,7 @@ class Map /// Required light grid is missing. @ingroup errors DENG2_ERROR(MissingLightGridError); - /// Attempted to add a new element when already full. @ingroup errors + /// Attempted to add a new element/object when full. @ingroup errors DENG2_ERROR(FullError); #endif @@ -109,12 +117,10 @@ class Map DENG2_DEFINE_AUDIENCE(Deletion, void mapBeingDeleted(Map const &map)) /// Notified when a one-way window construct is first found. - DENG2_DEFINE_AUDIENCE(OneWayWindowFound, - void oneWayWindowFound(Line &line, Sector &backFacingSector)) + DENG2_DEFINE_AUDIENCE(OneWayWindowFound, void oneWayWindowFound(Line &line, Sector &backFacingSector)) /// Notified when an unclosed sector is first found. - DENG2_DEFINE_AUDIENCE(UnclosedSectorFound, - void unclosedSectorFound(Sector §or, Vector2d const &nearPoint)) + DENG2_DEFINE_AUDIENCE(UnclosedSectorFound, void unclosedSectorFound(Sector §or, Vector2d const &nearPoint)) /* * Constants: @@ -126,33 +132,18 @@ class Map static int const MAX_GENERATORS = 512; #endif - /* - * Linked-element lists: - */ - typedef Mesh::Vertexes Vertexes; - typedef QList Lines; - typedef QList Polyobjs; - typedef QList Sectors; - - typedef QList Subspaces; + typedef de::BinaryTree BspTree; #ifdef __CLIENT__ - typedef QSet PlaneSet; - typedef QSet SurfaceSet; - - typedef QList BiasSources; - typedef QList Lumobjs; - - typedef QHash ClMobjHash; + typedef QSet PlaneSet; + typedef QSet SurfaceSet; + typedef QHash ClMobjHash; #endif - typedef de::BinaryTree BspTree; - public: /// @todo make private: - coord_t _globalGravity; // The defined gravity for this map. - coord_t _effectiveGravity; // The effective gravity for this map. - - int _ambientLightLevel; // Ambient lightlevel for the current map. + coord_t _globalGravity = 0; ///< The defined gravity for this map. + coord_t _effectiveGravity = 0; ///< The effective gravity for this map. + int _ambientLightLevel = 0; ///< Ambient lightlevel for the current map. public: /** @@ -163,12 +154,7 @@ class Map * * @param mapDefinition Definition for the map (Can be set later, @ref setDef). */ - Map(MapDef *mapDefinition = 0); - - /** - * Change the definition associated with the map to @a newMapDefinition. - */ - void setDef(MapDef *newMapDefinition); + explicit Map(MapDef *mapDefinition = nullptr); /** * Returns the definition for the map. @@ -176,9 +162,9 @@ class Map MapDef *def() const; /** - * To be called following an engine reset to update the map state. + * Change the definition associated with the map to @a newMapDefinition. */ - void update(); + void setDef(MapDef *newMapDefinition); /** * Returns the points which describe the boundary of the map coordinate @@ -195,6 +181,11 @@ class Map return Vector2d(bounds().max) - Vector2d(bounds().min); } + /** + * Returns the minimum ambient light level for the whole map. + */ + int ambientLightLevel() const; + /** * Returns the currently effective gravity multiplier for the map. */ @@ -208,187 +199,221 @@ class Map void setGravity(coord_t newGravity); /** - * Returns the minimum ambient light level for the whole map. + * To be called following an engine reset to update the map state. */ - int ambientLightLevel() const; + void update(); + +#ifdef __CLIENT__ +public: // Light sources ---------------------------------------------------------- /** - * Provides access to the thinker lists for the map. + * Returns the total number of BiasSources in the map. */ - Thinkers /*const*/ &thinkers() const; + int biasSourceCount() const; /** - * Returns the logical sky for the map. + * Attempt to add a new bias light source to the map (a copy is made). + * + * @note At most @ref MAX_BIAS_SOURCES are supported for technical reasons. + * + * @return Reference to the newly added bias source. + * + * @see biasSourceCount() + * @throws FullError Once capacity is reached. */ - Sky &sky() const; + BiasSource &addBiasSource(BiasSource const &biasSource = BiasSource()); /** - * Provides access to the primary @ref Mesh geometry owned by the map. - * Note that further meshes may be assigned to individual elements of - * the map should their geometries not be representable as a manifold - * with the primary mesh (e.g., polyobjs and BSP leaf "extra" meshes). + * Removes the specified bias light source from the map. + * + * @see removeAllBiasSources() */ - Mesh const &mesh() const; + void removeBiasSource(int which); /** - * Provides a list of all the non-editable vertexes in the map. + * Remove all bias sources from the map. + * + * @see removeBiasSource() */ - Vertexes const &vertexes() const; + void removeAllBiasSources(); /** - * Provides a list of all the non-editable lines in the map. + * Lookup a BiasSource by it's unique @a index. */ - Lines const &lines() const; + BiasSource &biasSource(int index) const; + BiasSource *biasSourcePtr(int index) const; /** - * Provides a list of all the non-editable polyobjs in the map. + * Finds the bias source nearest to the specified map space @a point. + * + * @note This result is not cached. May return @c 0 if no bias sources exist. */ - Polyobjs const &polyobjs() const; + BiasSource *biasSourceNear(Vector3d const &point) const; /** - * Provides a list of all the non-editable sectors in the map. + * Iterate through the BiasSources of the map. */ - Sectors const §ors() const; - - inline int vertexCount() const { return vertexes().count(); } + LoopResult forAllBiasSources(std::function func) const; - inline int lineCount() const { return lines().count(); } - - inline int sideCount() const { return lines().count() * 2; } - - inline int polyobjCount() const { return polyobjs().count(); } - - inline int sectorCount() const { return sectors().count(); } + /** + * Lookup the unique index for the given bias @a source. + */ + int indexOf(BiasSource const &source) const; /** - * Provides access to the subspace list for efficient traversal. + * Returns the time in milliseconds when the current render frame began. Used + * for interpolation purposes. */ - Subspaces const &subspaces() const; + uint biasCurrentTime() const; /** - * Returns the total number of subspaces in the map. + * Returns the frameCount of the current render frame. Used for tracking changes + * to bias sources/surfaces. */ - inline int subspaceCount() const { return subspaces().count(); } + uint biasLastChangeOnFrame() const; + + // Luminous-objects ----------------------------------------------------------- /** - * Returns the total number of SectorClusters in the map. + * Returns the total number of lumobjs in the map. */ - int clusterCount() const; + int lumobjCount() const; /** - * Iterate through the SectorClusters of the map. + * Add a new lumobj to the map (a copy is made). * - * @param sector If not @c nullptr, traverse the clusters of this Sector only. + * @return Reference to the newly added lumobj. */ - LoopResult forAllClusters(Sector *sector, std::function func); - - inline LoopResult forAllClusters(std::function func) { - return forAllClusters(nullptr, func); - } + Lumobj &addLumobj(Lumobj const &lumobj = Lumobj()); /** - * Helper function which returns the relevant side index given a @a lineIndex - * and @a side identifier. - * - * Indices are produced as follows: - * @code - * lineIndex / 2 + (backSide? 1 : 0); - * @endcode - * - * @param lineIndex Index of the line in the map. - * @param side Side of the line. @c =0 the Line::Front else Line::Back + * Removes the specified lumobj from the map. * - * @return Unique index for the identified side. + * @see removeAllLumobjs() */ - static int toSideIndex(int lineIndex, int side); + void removeLumobj(int which); /** - * Locate a LineSide in the map by it's unique @a index. - * - * @param index Unique index attributed to the line side. - * - * @return Pointer to the identified LineSide instance; otherwise @c 0. + * Remove all lumobjs from the map. * - * @see toSideIndex() + * @see removeLumobj() */ - LineSide *sideByIndex(int index) const; + void removeAllLumobjs(); /** - * Locate a Polyobj in the map by it's unique in-map tag. - * - * @param tag Tag associated with the polyobj to be located. - * - * @return Pointer to the identified Polyobj instance; otherwise @c 0. + * Lookup a Lumobj in the map by it's unique @a index. */ - Polyobj *polyobjByTag(int tag) const; + Lumobj &lumobj(int index) const; + Lumobj *lumobjPtr(int index) const; /** - * Provides access to the entity database. + * Iterate through the Lumpobjs of the map. */ - EntityDatabase &entityDatabase() const; + LoopResult forAllLumobjs(std::function func) const; +#endif // __CLIENT__ + +public: // Lines & Line-Sides ----------------------------------------------------- /** - * Provides access to the mobj blockmap. + * Returns the total number of Lines in the map. */ - Blockmap const &mobjBlockmap() const; + int lineCount() const; /** - * Provides access to the line blockmap. + * Lookup a Line in the map by it's unique @a index. */ - LineBlockmap const &lineBlockmap() const; + Line &line(int index) const; + Line *linePtr(int index) const; /** - * Provides access to the polyobj blockmap. + * Iterate through the Lines of the map. */ - Blockmap const &polyobjBlockmap() const; + LoopResult forAllLines(std::function func) const; /** - * Provides access to the convex subspace blockmap. + * Lines and Polyobj lines (note polyobj lines are iterated first). + * + * @note validCount should be incremented before calling this to begin a new + * logical traversal. Otherwise Lines marked with a validCount equal to this will + * be skipped over (can be used to avoid processing a line multiple times during + * complex / non-linear traversals. + * + * @param flags @ref lineIteratorFlags */ - Blockmap const &subspaceBlockmap() const; + LoopResult forAllLinesInBox(AABoxd const &box, int flags, std::function func) const; + + /// @copydoc forAllLinesInBox() + inline LoopResult forAllLinesInBox(AABoxd const &box, std::function func) const + { + return forAllLinesInBox(box, LIF_ALL, func); + } /** - * Returns @c true iff a BSP tree is available for the map. + * The callback function will be called once for each line that crosses + * trough the object. This means all the lines will be two-sided. */ - bool hasBspTree() const; + int forAllLinesTouchingMobj(struct mobj_s *mobj, int (*callback) (Line *, void *), + void *context = nullptr) const; /** - * Provides access to map's BSP tree, for efficient traversal. + * @param flags @ref lineIteratorFlags */ - BspTree const &bspTree() const; + int forAllLinesInPath(Vector2d const &from, Vector2d const &to, int flags, + int (*callback) (Line *line, void *context), void *context = nullptr) const; + + /// @copydoc forAllLinesInPath() + inline int forAllLinesInPath(Vector2d const &from, Vector2d const &to, + int (*callback) (Line *line, void *context), void *context = nullptr) const + { + return forAllLinesInPath(from, to, LIF_ALL, callback, context); + } + + // --- /** - * Determine the BSP leaf on the back side of the BS partition that lies - * in front of the specified point within the map's coordinate space. - * - * @note Always returns a valid BspLeaf although the point may not actually - * lay within it (however it is on the same side of the space partition)! - * - * @param point Map space coordinates to determine the BSP leaf for. - * - * @return BspLeaf instance for that BSP node's leaf. + * Returns the total number of Line::Sides in the map. */ - BspLeaf &bspLeafAt(Vector2d const &point) const; + inline int sideCount() const { return lineCount() * 2; } /** - * @copydoc bspLeafAt() + * Lookup a LineSide in the map by it's unique @a index. * - * The test is carried out using fixed-point math for behavior compatible - * with vanilla DOOM. Note that this means there is a maximum size for the - * point: it cannot exceed the fixed-point 16.16 range (about 65k units). + * @see toSideIndex() */ - BspLeaf &bspLeafAt_FixedPrecision(Vector2d const &point) const; + LineSide &side(int index) const; + LineSide *sidePtr(int index) const; /** - * Determine the SectorCluster which contains @a point and which is on the - * back side of the BS partition that lies in front of @a point. + * Helper function which returns the relevant side index given a @a lineIndex + * and @a side identifier. * - * @param point Map space coordinates to determine the BSP leaf for. + * Indices are produced as follows: + * @code + * lineIndex / 2 + (backSide? 1 : 0); + * @endcode * - * @return SectorCluster containing the specified point if any or @c 0 if - * the clusters have not yet been built. + * @param lineIndex Index of the line in the map. + * @param side Side of the line. @c =0 the Line::Front else Line::Back + * + * @return Unique index for the identified side. */ - SectorCluster *clusterAt(Vector2d const &point) const; + static int toSideIndex(int lineIndex, int side); + +public: // Map-objects ------------------------------------------------------------ + + int forAllMobjsTouchingLine(Line *line, int (*callback) (struct mobj_s *mobj, void *context), + void *context = nullptr) const; + + /** + * Increment validCount before using this. 'func' is called for each mobj + * that is (even partly) inside the sector. This is not a 3D test, the + * mobjs may actually be above or under the sector. + * + * (Lovely name; actually this is a combination of SectorMobjs and + * a bunch of LineMobjs iterations.) + */ + int forAllMobjsTouchingSector(Sector *sector, int (*callback) (struct mobj_s *mobj, void *context), + void *context = nullptr) const; /** * Links a mobj into both a block and a BSP leaf based on it's (x,y). @@ -398,108 +423,113 @@ class Map */ void link(struct mobj_s &mobj, int flags); - /** - * Link the specified @a polyobj in any internal data structures for - * bookkeeping purposes. Should be called AFTER Polyobj rotation and/or - * translation to (re-)insert the polyobj. - * - * @param polyobj Polyobj to be linked. - */ - void link(Polyobj &polyobj); - /** * Unlinks a mobj from everything it has been linked to. Should be called * BEFORE mobj translation to extract the mobj. * - * @param mo Mobj to be unlinked. + * @param mobj Mobj to be unlinked. * * @return DDLINK_* flags denoting what the mobj was unlinked from * (in case we need to re-link). */ int unlink(struct mobj_s &mobj); +#ifdef __CLIENT__ +public: // Particle generators -------------------------------------------------------- + /** - * Unlink the specified @a polyobj from any internal data structures for - * bookkeeping purposes. Should be called BEFORE Polyobj rotation and/or - * translation to extract the polyobj. + * Returns the total number of @em active generators in the map. + */ + int generatorCount() const; + + /** + * Attempt to spawn a new (particle) generator for the map. If no free identifier + * is available then @c 0 is returned. + */ + Generator *newGenerator(); + + /** + * Iterate over all generators in the map making a callback for each. Iteration + * ends when all generators have been processed or a callback returns non-zero. + * + * @param callback Callback to make for each iteration. + * @param context User data to be passed to the callback. * - * @param polyobj Polyobj to be unlinked. + * @return @c 0 iff iteration completed wholly. */ - void unlink(Polyobj &polyobj); + int generatorIterator(int (*callback) (Generator *, void *), void *context = nullptr); /** - * Given an @a emitter origin, attempt to identify the map element - * to which it belongs. + * Iterate over all generators in the map which are present in the identified + * list making a callback for each. Iteration ends when all targeted generators + * have been processed or a callback returns non-zero. * - * @param emitter The sound emitter to be identified. - * @param sector The identified sector if found is written here. - * @param poly The identified polyobj if found is written here. - * @param plane The identified plane if found is written here. - * @param surface The identified line side surface if found is written here. + * @param listIndex Index of the list to traverse. + * @param callback Callback to make for each iteration. + * @param context User data to be passed to the callback. * - * @return @c true iff @a emitter is an identifiable map element. + * @return @c 0 iff iteration completed wholly. */ - bool identifySoundEmitter(ddmobj_base_t const &emitter, Sector **sector, - Polyobj **poly, Plane **plane, Surface **surface) const; + int generatorListIterator(uint listIndex, int (*callback) (Generator *, void *), + void *context = nullptr); - int mobjBoxIterator(AABoxd const &box, - int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const; + void unlink(Generator &generator); - int mobjPathIterator(Vector2d const &from, Vector2d const &to, - int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const; +#endif // __CLIENT__ + +public: // Poly objects ----------------------------------------------------------- + + /** + * Returns the total number of Polyobjs in the map. + */ + int polyobjCount() const; + + /** + * Lookup a Polyobj in the map by it's unique @a index. + */ + Polyobj &polyobj(int index) const; + Polyobj *polyobjPtr(int index) const; + + /** + * Iterate through the Sectors of the map. + */ + LoopResult forAllPolyobjs(std::function func) const; + + /** + * Link the specified @a polyobj in any internal data structures for + * bookkeeping purposes. Should be called AFTER Polyobj rotation and/or + * translation to (re-)insert the polyobj. + * + * @param polyobj Poly-object to be linked. + */ + void link(Polyobj &polyobj); /** - * Lines and Polyobj lines (note polyobj lines are iterated first). - * - * @note validCount should be incremented before calling this to begin - * a new logical traversal. Otherwise Lines marked with a validCount - * equal to this will be skipped over (can be used to avoid processing - * a line multiple times during complex / non-linear traversals. + * Unlink the specified @a polyobj from any internal data structures for + * bookkeeping purposes. Should be called BEFORE Polyobj rotation and/or + * translation to extract the polyobj. * - * @param flags @ref lineIteratorFlags + * @param polyobj Poly-object to be unlinked. */ - int lineBoxIterator(AABoxd const &box, int flags, - int (*callback) (Line *line, void *context), void *context = 0) const; + void unlink(Polyobj &polyobj); - /// @copydoc lineBoxIterator() - inline int lineBoxIterator(AABoxd const &box, - int (*callback) (Line *line, void *context), void *context = 0) const - { - return lineBoxIterator(box, LIF_ALL, callback, context); - } +public: // Sectors ---------------------------------------------------------------- /** - * @param flags @ref lineIteratorFlags + * Returns the total number of Sectors in the map. */ - int linePathIterator(Vector2d const &from, Vector2d const &to, int flags, - int (*callback) (Line *line, void *context), void *context = 0) const; - - /// @copydoc linePathIterator() - inline int linePathIterator(Vector2d const &from, Vector2d const &to, - int (*callback) (Line *line, void *context), void *context = 0) const - { - return linePathIterator(from, to, LIF_ALL, callback, context); - } - - int subspaceBoxIterator(AABoxd const &box, - int (*callback) (ConvexSubspace *subspace, void *context), void *context = 0) const; + int sectorCount() const; /** - * @note validCount should be incremented before calling this to begin a - * new logical traversal. Otherwise Lines marked with a validCount equal - * to this will be skipped over (can be used to avoid processing a line - * multiple times during complex / non-linear traversals. + * Lookup a Sector in the map by it's unique @a index. */ - int polyobjBoxIterator(AABoxd const &box, - int (*callback) (struct polyobj_s *polyobj, void *context), - void *context = 0) const; + Sector §or(int index) const; + Sector *sectorPtr(int index) const; /** - * The callback function will be called once for each line that crosses - * trough the object. This means all the lines will be two-sided. + * Iterate through the Sectors of the map. */ - int mobjTouchedLineIterator(struct mobj_s *mo, - int (*callback) (Line *, void *), void *context = 0) const; + LoopResult forAllSectors(std::function func) const; /** * Increment validCount before calling this routine. The callback function @@ -507,22 +537,44 @@ class Map * partly inside). This is not a 3D check; the mobj may actually reside * above or under the sector. */ - int mobjTouchedSectorIterator(struct mobj_s *mo, - int (*callback) (Sector *sector, void *context), void *context = 0) const; + int forAllSectorsTouchingMobj(struct mobj_s *mobj, int (*callback) (Sector *sector, void *context), + void *context = nullptr) const; - int lineTouchingMobjIterator(Line *line, - int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const; +public: // Sector clusters -------------------------------------------------------- /** - * Increment validCount before using this. 'func' is called for each mobj - * that is (even partly) inside the sector. This is not a 3D test, the - * mobjs may actually be above or under the sector. + * Returns the total number of SectorClusters in the map. + */ + int clusterCount() const; + + /** + * Determine the SectorCluster which contains @a point and which is on the + * back side of the BS partition that lies in front of @a point. * - * (Lovely name; actually this is a combination of SectorMobjs and - * a bunch of LineMobjs iterations.) + * @param point Map space coordinates to determine the BSP leaf for. + * + * @return SectorCluster containing the specified point if any or @c 0 if + * the clusters have not yet been built. + */ + SectorCluster *clusterAt(Vector2d const &point) const; + + /** + * Iterate through the SectorClusters of the map. + * + * @param sector If not @c nullptr, traverse the clusters of this Sector only. + */ + LoopResult forAllClusters(Sector *sector, std::function func); + + inline LoopResult forAllClusters(std::function func) { + return forAllClusters(nullptr, func); + } + +public: // Skies ------------------------------------------------------------------ + + /** + * Returns the logical sky for the map. */ - int sectorTouchingMobjIterator(Sector *sector, - int (*callback) (struct mobj_s *mobj, void *context), void *context = 0) const; + Sky &sky() const; #ifdef __CLIENT__ coord_t skyFix(bool ceiling) const; @@ -538,152 +590,160 @@ class Map inline void setSkyFixCeiling(coord_t newHeight) { setSkyFix(true /*the ceiling*/, newHeight); } +#endif + +public: // Subspaces -------------------------------------------------------------- /** - * Attempt to spawn a new (particle) generator for the map. If no free identifier - * is available then @c 0 is returned. + * Returns the total number of subspaces in the map. */ - Generator *newGenerator(); - - void unlink(Generator &generator); + int subspaceCount() const; /** - * Iterate over all generators in the map making a callback for each. Iteration - * ends when all generators have been processed or a callback returns non-zero. - * - * @param callback Callback to make for each iteration. - * @param context User data to be passed to the callback. - * - * @return @c 0 iff iteration completed wholly. + * Lookup a Subspace in the map by it's unique @a index. */ - int generatorIterator(int (*callback) (Generator *, void *), void *context = 0); + ConvexSubspace &subspace(int index) const; + ConvexSubspace *subspacePtr(int index) const; /** - * Iterate over all generators in the map which are present in the identified - * list making a callback for each. Iteration ends when all targeted generators - * have been processed or a callback returns non-zero. - * - * @param listIndex Index of the list to traverse. - * @param callback Callback to make for each iteration. - * @param context User data to be passed to the callback. - * - * @return @c 0 iff iteration completed wholly. + * Iterate through the Sectors of the map. */ - int generatorListIterator(uint listIndex, int (*callback) (Generator *, void *), void *context = 0); + LoopResult forAllSubspaces(std::function func) const; + +public: // Vertexs ---------------------------------------------------------------- /** - * Returns the total number of @em active generators in the map. + * Returns the total number of Vertexs in the map. */ - int generatorCount() const; + int vertexCount() const; /** - * Add a new lumobj to the map (a copy is made). - * - * @return Reference to the newly added lumobj. - * - * @see lumobjCount() + * Lookup a Vertex in the map by it's unique @a index. */ - Lumobj &addLumobj(Lumobj const &lumobj = Lumobj()); + Vertex &vertex(int index) const; + Vertex *vertexPtr(int index) const; /** - * Removes the specified lumobj from the map. - * - * @see removeAllLumobjs() + * Iterate through the Vertexs of the map. */ - void removeLumobj(int which); + LoopResult forAllVertexs(std::function func) const; + +public: // Data structures -------------------------------------------------------- /** - * Remove all lumobjs from the map. - * - * @see removeLumobj() + * Provides access to the entity database. */ - void removeAllLumobjs(); + EntityDatabase &entityDatabase() const; /** - * Provides a list of all the lumobjs in the map. + * Provides access to the primary @ref Mesh geometry owned by the map. Note that + * further meshes may be assigned to individual elements of the map should their + * geometries not be representable as a manifold with the primary mesh (e.g., + * polyobjs and BSP leaf "extra" meshes). */ - Lumobjs const &lumobjs() const; + Mesh const &mesh() const; /** - * Returns the total number of lumobjs in the map. + * Provides access to the line blockmap. */ - inline int lumobjCount() const { return lumobjs().count(); } + LineBlockmap const &lineBlockmap() const; /** - * Lookup a lumobj in the map by it's unique @a index. + * Provides access to the mobj blockmap. */ - inline Lumobj *lumobj(int index) const { return lumobjs().at(index); } + Blockmap const &mobjBlockmap() const; /** - * Attempt to add a new bias light source to the map (a copy is made). - * - * @note At most @ref MAX_BIAS_SOURCES are supported for technical reasons. - * - * @return Reference to the newly added bias source. - * - * @see biasSourceCount() - * @throws FullError Once capacity is reached. + * Provides access to the polyobj blockmap. */ - BiasSource &addBiasSource(BiasSource const &biasSource = BiasSource()); + Blockmap const &polyobjBlockmap() const; /** - * Removes the specified bias light source from the map. - * - * @see removeAllBiasSources() + * Provides access to the convex subspace blockmap. */ - void removeBiasSource(int which); + Blockmap const &subspaceBlockmap() const; /** - * Remove all bias sources from the map. - * - * @see removeBiasSource() + * Provides access to the thinker lists for the map. */ - void removeAllBiasSources(); + Thinkers /*const*/ &thinkers() const; /** - * Provides a list of all the bias sources in the map. + * Returns @c true iff a BSP tree is available for the map. */ - BiasSources const &biasSources() const; + bool hasBspTree() const; /** - * Returns the total number of bias sources in the map. + * Provides access to map's BSP tree, for efficient traversal. */ - inline int biasSourceCount() const { return biasSources().count(); } + BspTree const &bspTree() const; /** - * Returns the time in milliseconds when the current render frame began. Used - * for interpolation purposes. + * Determine the BSP leaf on the back side of the BS partition that lies + * in front of the specified point within the map's coordinate space. + * + * @note Always returns a valid BspLeaf although the point may not actually + * lay within it (however it is on the same side of the space partition)! + * + * @param point Map space coordinates to determine the BSP leaf for. + * + * @return BspLeaf instance for that BSP node's leaf. */ - uint biasCurrentTime() const; + BspLeaf &bspLeafAt(Vector2d const &point) const; /** - * Returns the frameCount of the current render frame. Used for tracking changes - * to bias sources/surfaces. + * @copydoc bspLeafAt() + * + * The test is carried out using fixed-point math for behavior compatible + * with vanilla DOOM. Note that this means there is a maximum size for the + * point: it cannot exceed the fixed-point 16.16 range (about 65k units). */ - uint biasLastChangeOnFrame() const; + BspLeaf &bspLeafAt_FixedPrecision(Vector2d const &point) const; /** - * Lookup a bias source in the map by it's unique @a index. + * Given an @a emitter origin, attempt to identify the map element + * to which it belongs. + * + * @param emitter The sound emitter to be identified. + * @param sector The identified sector if found is written here. + * @param poly The identified polyobj if found is written here. + * @param plane The identified plane if found is written here. + * @param surface The identified line side surface if found is written here. + * + * @return @c true iff @a emitter is an identifiable map element. */ - BiasSource *biasSource(int index) const; + bool identifySoundEmitter(ddmobj_base_t const &emitter, Sector **sector, + Polyobj **poly, Plane **plane, Surface **surface) const; + +#ifdef __CLIENT__ /** - * Finds the bias source nearest to the specified map space @a point. + * Returns @c true iff a LightGrid has been initialized for the map. * - * @note This result is not cached. May return @c 0 if no bias sources exist. + * @see lightGrid() */ - BiasSource *biasSourceNear(Vector3d const &point) const; + bool hasLightGrid(); /** - * Lookup the unique index for the given bias @a source. + * Provides access to the light grid for the map. + * + * @see hasLightGrid() */ - int toIndex(BiasSource const &source) const; + LightGrid &lightGrid(); /** - * Deletes hidden, unpredictable or nulled mobjs for which we have not received - * updates in a while. + * (Re)-initialize the light grid used for smoothed sector lighting. + * + * If the grid has not yet been initialized block light sources are determined + * at this time (SectorClusters must be built for this). + * + * If the grid has already been initialized calling this will perform a full update. + * + * @note Initialization may take some time depending on the complexity of the + * map (physial dimensions, number of sectors) and should therefore be done + * "off-line". */ - void expireClMobjs(); + void initLightGrid(); /** * Link the given @a surface in all material lists and surface sets which @@ -727,41 +787,26 @@ class Map void updateTrackedPlanes(); /** - * Returns @c true iff a LightGrid has been initialized for the map. - * - * @see lightGrid() + * Perform spreading of all contacts in the specified map space @a region. */ - bool hasLightGrid(); + void spreadAllContacts(AABoxd const ®ion); - /** - * Provides access to the light grid for the map. - * - * @see hasLightGrid() - */ - LightGrid &lightGrid(); +#endif // __CLIENT__ + +public: /** - * (Re)-initialize the light grid used for smoothed sector lighting. - * - * If the grid has not yet been initialized block light sources are determined - * at this time (SectorClusters must be built for this). - * - * If the grid has already been initialized calling this will perform a full update. - * - * @note Initialization may take some time depending on the complexity of the - * map (physial dimensions, number of sectors) and should therefore be done - * "off-line". + * Returns a rich formatted, textual summary of the map's elements, suitable + * for logging. */ - void initLightGrid(); + String elementSummaryAsStyledText() const; /** - * Perform spreading of all contacts in the specified map space @a region. + * Returns a rich formatted, textual summary of the map's objects, suitable + * for logging. */ - void spreadAllContacts(AABoxd const ®ion); - -#endif // __CLIENT__ + String objectSummaryAsStyledText() const; -public: /** * To be called to register the commands and variables of this module. */ @@ -773,15 +818,7 @@ class Map */ static void initDummies(); -#ifdef __CLIENT__ - -protected: - /// Observes WorldSystem FrameBegin - void worldSystemFrameBegins(bool resetNextViewer); - -#endif // __CLIENT__ - -public: /// @todo Make private: +public: /// @todo Most of the following should be private: /** * Initialize the node piles and link rings. To be called after map load. @@ -842,6 +879,12 @@ class Map */ void clearClMobjs(); + /** + * Deletes hidden, unpredictable or nulled mobjs for which we have not received + * updates in a while. + */ + void expireClMobjs(); + /** * Find/create a client mobj with the unique identifier @a id. Client mobjs are * just like normal mobjs, except they have additional network state. @@ -854,7 +897,7 @@ class Map * * @return Pointer to the gameside mobj. */ - mobj_t *clMobjFor(thid_t id, bool canCreate = false) const; + struct mobj_s *clMobjFor(thid_t id, bool canCreate = false) const; /** * Iterate all client mobjs, making a callback for each. Iteration ends if a @@ -865,30 +908,20 @@ class Map * * @return @c 0 if all callbacks return @c 0; otherwise the result of the last. */ - int clMobjIterator(int (*callback) (mobj_t *, void *), void *context = 0); + int clMobjIterator(int (*callback) (struct mobj_s *, void *), void *context = nullptr); /** * Provides readonly access to the client mobj hash. */ ClMobjHash const &clMobjHash() const; -#endif // __CLIENT__ - /** - * Returns a rich formatted, textual summary of the map's elements, suitable - * for logging. - */ - de::String elementSummaryAsStyledText() const; +protected: + /// Observes WorldSystem FrameBegin + void worldSystemFrameBegins(bool resetNextViewer); - /** - * Returns a rich formatted, textual summary of the map's objects, suitable - * for logging. - */ - de::String objectSummaryAsStyledText() const; +#endif // __CLIENT__ -public: - /* - * Runtime map editing: - */ +public: // Editing ---------------------------------------------------------------- /** * Returns @c true iff the map is currently in an editable state. @@ -913,7 +946,7 @@ class Map * @see isEditable() */ Line *createLine(Vertex &v1, Vertex &v2, int flags = 0, - Sector *frontSector = 0, Sector *backSector = 0, + Sector *frontSector = nullptr, Sector *backSector = nullptr, int archiveIndex = MapElement::NoIndex); /** @@ -930,16 +963,19 @@ class Map /** * Provides a list of all the editable lines in the map. */ + typedef QList Lines; Lines const &editableLines() const; /** * Provides a list of all the editable polyobjs in the map. */ + typedef QList Polyobjs; Polyobjs const &editablePolyobjs() const; /** * Provides a list of all the editable sectors in the map. */ + typedef QList Sectors; Sectors const &editableSectors() const; inline int editableLineCount() const { return editableLines().count(); } diff --git a/doomsday/client/include/world/plane.h b/doomsday/client/include/world/plane.h index cdf9f84e58..51fafc70cb 100644 --- a/doomsday/client/include/world/plane.h +++ b/doomsday/client/include/world/plane.h @@ -1,7 +1,7 @@ /** @file plane.h World map plane. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -21,19 +21,18 @@ #ifndef DENG_WORLD_PLANE_H #define DENG_WORLD_PLANE_H -#include "dd_share.h" // SoundEmitter -#ifdef __CLIENT__ -# include "def_main.h" -#endif - -#include "MapElement" - #ifdef __CLIENT__ # include #endif #include #include +#include "dd_share.h" // SoundEmitter +#ifdef __CLIENT__ +# include "def_main.h" // ded_ptcgen_t +#endif +#include "MapElement" + class Sector; class Surface; #ifdef __CLIENT__ @@ -58,15 +57,15 @@ class Plane : public de::MapElement #endif /// Notified when the plane is about to be deleted. - DENG2_DEFINE_AUDIENCE(Deletion, void planeBeingDeleted(Plane const &plane)) + DENG2_DEFINE_AUDIENCE2(Deletion, void planeBeingDeleted(Plane const &plane)) /// Notified whenever a @em sharp height change occurs. - DENG2_DEFINE_AUDIENCE(HeightChange, void planeHeightChanged(Plane &plane)) + DENG2_DEFINE_AUDIENCE2(HeightChange, void planeHeightChanged(Plane &plane)) #ifdef __CLIENT__ /// Notified whenever a @em smoothed height change occurs. - DENG2_DEFINE_AUDIENCE(HeightSmoothedChange, void planeHeightSmoothedChanged(Plane &plane)) + DENG2_DEFINE_AUDIENCE2(HeightSmoothedChange, void planeHeightSmoothedChanged(Plane &plane)) #endif @@ -88,9 +87,7 @@ class Plane : public de::MapElement /** * Returns the owning Sector of the plane. */ - Sector §or(); - - /// @copydoc sector() + Sector §or(); Sector const §or() const; /** @@ -118,9 +115,7 @@ class Plane : public de::MapElement /** * Returns the Surface of the plane. */ - Surface &surface(); - - /// @copydoc surface() + Surface &surface(); Surface const &surface() const; /** @@ -135,9 +130,7 @@ class Plane : public de::MapElement /** * Returns the sound emitter for the plane. */ - SoundEmitter &soundEmitter(); - - /// @copydoc soundEmitter() + SoundEmitter &soundEmitter(); SoundEmitter const &soundEmitter() const; /** diff --git a/doomsday/client/include/world/surface.h b/doomsday/client/include/world/surface.h index 3aeb232827..2f8f0b79ea 100644 --- a/doomsday/client/include/world/surface.h +++ b/doomsday/client/include/world/surface.h @@ -1,7 +1,7 @@ /** @file surface.h World map surface. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -21,20 +21,15 @@ #ifndef DENG_WORLD_SURFACE_H #define DENG_WORLD_SURFACE_H -#include "Material" - -#include "MapElement" -#include - +#include #include #include #include #include -#ifdef __CLIENT__ -# include -#endif +#include +#include "MapElement" +#include "Material" -class BspLeaf; class Decoration; /** @@ -53,28 +48,20 @@ class Surface : public de::MapElement DENG2_ERROR(MissingMaterialError); /// Notified when the @em sharp material origin changes. - DENG2_DEFINE_AUDIENCE(MaterialOriginChange, void surfaceMaterialOriginChanged(Surface &surface)) + DENG2_DEFINE_AUDIENCE2(MaterialOriginChange, void surfaceMaterialOriginChanged(Surface &surface)) /// Notified whenever the normal vector changes. - DENG2_DEFINE_AUDIENCE(NormalChange, void surfaceNormalChanged(Surface &surface)) + DENG2_DEFINE_AUDIENCE2(NormalChange, void surfaceNormalChanged(Surface &surface)) /// Notified whenever the opacity changes. - DENG2_DEFINE_AUDIENCE(OpacityChange, void surfaceOpacityChanged(Surface &surface)) + DENG2_DEFINE_AUDIENCE2(OpacityChange, void surfaceOpacityChanged(Surface &surface)) /// Notified whenever the tint color changes. - DENG2_DEFINE_AUDIENCE(TintColorChange, void surfaceTintColorChanged(Surface §or)) + DENG2_DEFINE_AUDIENCE2(TintColorChange, void surfaceTintColorChanged(Surface §or)) /// Maximum speed for a smoothed material offset. static int const MAX_SMOOTH_MATERIAL_MOVE = 8; -#ifdef __CLIENT__ - typedef QList Decorations; - -public: /// @todo Does not belong at this level - bool _needDecorationUpdate; ///< @c true= An update is needed. - -#endif // __CLIENT__ - public: /** * Construct a new surface. @@ -96,7 +83,7 @@ class Surface : public de::MapElement /** * Returns a copy of the normalized tangent vector for the surface. */ - inline de::Vector3f tangent() const { return tangentMatrix().column(0); } + inline de::Vector3f tangent() const { return tangentMatrix().column(0); } /** * Returns a copy of the normalized bitangent vector for the surface. @@ -106,7 +93,7 @@ class Surface : public de::MapElement /** * Returns a copy of the normalized normal vector for the surface. */ - inline de::Vector3f normal() const { return tangentMatrix().column(2); } + inline de::Vector3f normal() const { return tangentMatrix().column(2); } /** * Change the tangent space normal vector for the surface. If changed, @@ -182,11 +169,11 @@ class Surface : public de::MapElement Material &material() const; /** - * Returns a pointer to the attributed material of the surface; otherwise @c 0. + * Returns a pointer to the attributed material of the surface; otherwise @c nullptr. * * @see hasMaterial(), hasFixMaterial() */ - inline Material *materialPtr() const { return hasMaterial()? &material() : 0; } + inline Material *materialPtr() const { return hasMaterial()? &material() : nullptr; } /** * Change the attributed material of the surface. On client side, any existing @@ -315,31 +302,36 @@ class Surface : public de::MapElement float glow(de::Vector3f &color) const; /** - * Add the specified decoration to the surface. - * - * @param decoration Decoration to add. Ownership is given to the surface. + * Clear all surface decorations. */ - void addDecoration(Decoration *decoration); + void clearDecorations(); /** - * Clear all surface decorations. + * Returns the total number of surface decorations. */ - void clearDecorations(); + int decorationCount() const; /** - * Provides access to the surface decorations for efficient traversal. + * Add the specified decoration to the surface. + * + * @param decoration Decoration to add. Ownership is given to the surface. */ - Decorations const &decorations() const; + void addDecoration(Decoration *decoration); /** - * Returns the total number of surface decorations. + * Iterate through all the surface decorations. */ - int decorationCount() const; + de::LoopResult forAllDecorations(std::function func) const; /** * Mark the surface as needing a decoration update. */ - void markAsNeedingDecorationUpdate(); + void markForDecorationUpdate(bool yes = true); + + /** + * Returns @c true if the surface is marked for decoration update. + */ + bool needsDecorationUpdate() const; #endif // __CLIENT__ diff --git a/doomsday/client/include/world/worldsystem.h b/doomsday/client/include/world/worldsystem.h index 46ba43e804..617a9096f1 100644 --- a/doomsday/client/include/world/worldsystem.h +++ b/doomsday/client/include/world/worldsystem.h @@ -30,16 +30,18 @@ #ifndef DENG_WORLDSYSTEM_H #define DENG_WORLDSYSTEM_H -#include #include #include #include #include #include +#include #ifdef __CLIENT__ # include "render/skydrawable.h" +#endif +#ifdef __CLIENT__ class Hand; #endif @@ -57,14 +59,14 @@ class WorldSystem : public de::System DENG2_ERROR(MapError); /// Notified whenever the "current" map changes. - DENG2_DEFINE_AUDIENCE(MapChange, void worldSystemMapChanged()) + DENG2_DEFINE_AUDIENCE2(MapChange, void worldSystemMapChanged()) #ifdef __CLIENT__ /// Notified when a new frame begins. - DENG2_DEFINE_AUDIENCE(FrameBegin, void worldSystemFrameBegins(bool resetNextViewer)) + DENG2_DEFINE_AUDIENCE2(FrameBegin, void worldSystemFrameBegins(bool resetNextViewer)) /// Notified when the "current" frame ends. - DENG2_DEFINE_AUDIENCE(FrameEnd, void worldSystemFrameEnds()) + DENG2_DEFINE_AUDIENCE2(FrameEnd, void worldSystemFrameEnds()) #endif public: @@ -153,7 +155,7 @@ class WorldSystem : public de::System * @param distance The current distance of the hand from the viewer will be * written here if not @c 0. */ - Hand &hand(coord_t *distance = 0) const; + Hand &hand(coord_t *distance = nullptr) const; /** * Determines if a point is in the void. diff --git a/doomsday/client/src/client/cl_sound.cpp b/doomsday/client/src/client/cl_sound.cpp index f662dc0d87..14f7389136 100644 --- a/doomsday/client/src/client/cl_sound.cpp +++ b/doomsday/client/src/client/cl_sound.cpp @@ -73,12 +73,7 @@ void Cl_ReadSoundDelta(deltatype_t type) else if(type == DT_SECTOR_SOUND) // Plane as emitter { int index = deltaId; - - if(index >= 0 && index < map.sectorCount()) - { - sector = map.sectors().at(index); - } - else + if(!(sector = map.sectorPtr(index))) { LOG_NET_WARNING("Received sound delta has invalid sector index %i") << index; skip = true; @@ -87,9 +82,7 @@ void Cl_ReadSoundDelta(deltatype_t type) else if(type == DT_SIDE_SOUND) // Side section as emitter { int index = deltaId; - - side = map.sideByIndex(index); - if(!side) + if(!(side = map.sidePtr(index))) { LOG_NET_WARNING("Received sound delta has invalid side index %i") << index; skip = true; @@ -101,12 +94,7 @@ void Cl_ReadSoundDelta(deltatype_t type) LOG_NET_XVERBOSE("DT_POLY_SOUND: poly=%d") << index; - if(index >= 0 && index < map.polyobjCount()) - { - poly = map.polyobjs().at(index); - emitter = (mobj_t *) poly; - } - else + if(!(emitter = (mobj_t *) (poly = map.polyobjPtr(index)))) { LOG_NET_WARNING("Received sound delta has invalid polyobj index %i") << index; skip = true; @@ -283,7 +271,7 @@ void Cl_Sound() LOG_NET_WARNING("Invalid sector number %i") << num; return; } - mobj_t *mo = (mobj_t *) &map.sectors().at(num)->soundEmitter(); + mobj_t *mo = (mobj_t *) &map.sector(num).soundEmitter(); //S_StopSound(0, mo); S_LocalSoundAtVolume(sound, mo, volume / 127.0f); } diff --git a/doomsday/client/src/client/cl_world.cpp b/doomsday/client/src/client/cl_world.cpp index 7283bcfdc7..0f505a72c9 100644 --- a/doomsday/client/src/client/cl_world.cpp +++ b/doomsday/client/src/client/cl_world.cpp @@ -163,9 +163,8 @@ void Cl_ReadSectorDelta(int /*deltaType*/) float speed[2] = { 0, 0 }; // Sector index number. - int const index = Reader_ReadUInt16(msgReader); - DENG2_ASSERT(index < map.sectorCount()); - Sector *sec = map.sectors().at(index); + Sector *sec = map.sectorPtr(Reader_ReadUInt16(msgReader)); + DENG2_ASSERT(sec); // Flags. int df = Reader_ReadPackedUInt32(msgReader); @@ -266,7 +265,7 @@ void Cl_ReadSideDelta(int /*deltaType*/) int const index = Reader_ReadUInt16(msgReader); int const df = Reader_ReadPackedUInt32(msgReader); // Flags. - LineSide *side = map.sideByIndex(index); + LineSide *side = map.sidePtr(index); DENG2_ASSERT(side != 0); if(df & SIDF_TOP_MATERIAL) @@ -351,46 +350,42 @@ void Cl_ReadSideDelta(int /*deltaType*/) void Cl_ReadPolyDelta() { /// @todo Do not assume the CURRENT map. - Map &map = App_WorldSystem().map(); - - int const index = Reader_ReadPackedUInt16(msgReader); - int const df = Reader_ReadByte(msgReader); // Flags. - - DENG2_ASSERT(index < map.polyobjCount()); - Polyobj *po = map.polyobjs().at(index); + Map &map = App_WorldSystem().map(); + Polyobj &pob = map.polyobj(Reader_ReadPackedUInt16(msgReader)); + int const df = Reader_ReadByte(msgReader); // Flags. if(df & PODF_DEST_X) { - po->dest[VX] = Reader_ReadFloat(msgReader); + pob.dest[VX] = Reader_ReadFloat(msgReader); } if(df & PODF_DEST_Y) { - po->dest[VY] = Reader_ReadFloat(msgReader); + pob.dest[VY] = Reader_ReadFloat(msgReader); } if(df & PODF_SPEED) { - po->speed = Reader_ReadFloat(msgReader); + pob.speed = Reader_ReadFloat(msgReader); } if(df & PODF_DEST_ANGLE) { - po->destAngle = ((angle_t)Reader_ReadInt16(msgReader)) << 16; + pob.destAngle = ((angle_t)Reader_ReadInt16(msgReader)) << 16; } if(df & PODF_ANGSPEED) { - po->angleSpeed = ((angle_t)Reader_ReadInt16(msgReader)) << 16; + pob.angleSpeed = ((angle_t)Reader_ReadInt16(msgReader)) << 16; } if(df & PODF_PERPETUAL_ROTATE) { - po->destAngle = -1; + pob.destAngle = -1; } // Update/create the polymover thinker. - ClPolyMover::newThinker(*po, + ClPolyMover::newThinker(pob, /* move: */ CPP_BOOL(df & (PODF_DEST_X | PODF_DEST_Y | PODF_SPEED)), /* rotate: */ CPP_BOOL(df & (PODF_DEST_ANGLE | PODF_ANGSPEED | PODF_PERPETUAL_ROTATE))); } diff --git a/doomsday/client/src/dd_main.cpp b/doomsday/client/src/dd_main.cpp index 24108d47f9..cf767ecacb 100644 --- a/doomsday/client/src/dd_main.cpp +++ b/doomsday/client/src/dd_main.cpp @@ -3323,11 +3323,14 @@ DENG_EXTERN_C void R_SetupMap(int mode, int flags) #ifdef __CLIENT__ // Update all sectors. /// @todo Refactor away. - foreach(Sector *sector, map.sectors()) - foreach(LineSide *side, sector->sides()) + map.forAllSectors([] (Sector §or) { - side->fixMissingMaterials(); - } + for(LineSide *side : sector.sides()) + { + side->fixMissingMaterials(); + } + return LoopContinue; + }); #endif // Re-initialize polyobjs. diff --git a/doomsday/client/src/edit_bias.cpp b/doomsday/client/src/edit_bias.cpp index caf68f1ad7..f7706062ba 100644 --- a/doomsday/client/src/edit_bias.cpp +++ b/doomsday/client/src/edit_bias.cpp @@ -219,7 +219,7 @@ static void SBE_Grab(int which) { DENG_ASSERT(editActive); Hand &hand = App_WorldSystem().hand(); - if(BiasSource *source = App_WorldSystem().map().biasSource(which)) + if(BiasSource *source = App_WorldSystem().map().biasSourcePtr(which)) { if(hand.isEmpty()) { @@ -236,7 +236,7 @@ static void SBE_Ungrab(int which) { DENG_ASSERT(editActive); Hand &hand = App_WorldSystem().hand(); - if(BiasSource *source = App_WorldSystem().map().biasSource(which)) + if(BiasSource *source = App_WorldSystem().map().biasSourcePtr(which)) { hand.ungrab(*source); } @@ -250,14 +250,14 @@ static void SBE_SetLock(int which, bool enable = true) { DENG_ASSERT(editActive); Hand &hand = App_WorldSystem().hand(); - if(BiasSource *source = App_WorldSystem().map().biasSource(which)) + if(BiasSource *source = App_WorldSystem().map().biasSourcePtr(which)) { if(enable) source->lock(); else source->unlock(); return; } - foreach(Grabbable *grabbable, hand.grabbed()) + for(Grabbable *grabbable : hand.grabbed()) { if(enable) grabbable->lock(); else grabbable->unlock(); @@ -303,27 +303,27 @@ static bool SBE_Save(char const *name = 0) << NativePath(Str_Text(&fileName)).pretty(); String uid = (map.def()? map.def()->composeUniqueId(App_CurrentGame()) : "(unknown map)"); - fprintf(file, "# %i Bias Lights for %s\n\n", map.biasSourceCount(), uid.toUtf8().constData()); + fprintf(file, "# %i Bias Lights for %s", map.biasSourceCount(), uid.toUtf8().constData()); // Since there can be quite a lot of these, make sure we'll skip // the ones that are definitely not suitable. - fprintf(file, "SkipIf Not %s\n", App_CurrentGame().identityKey().toUtf8().constData()); + fprintf(file, "\n\nSkipIf Not %s", App_CurrentGame().identityKey().toUtf8().constData()); - foreach(BiasSource *src, map.biasSources()) + map.forAllBiasSources([&file, &uid] (BiasSource &bsrc) { - fprintf(file, "\nLight {\n"); - fprintf(file, " Map = \"%s\"\n", uid.toUtf8().constData()); - fprintf(file, " Origin { %g %g %g }\n", - src->origin().x, src->origin().y, src->origin().z); - fprintf(file, " Color { %g %g %g }\n", - src->color().x, src->color().y, src->color().z); - fprintf(file, " Intensity = %g\n", src->intensity()); - float minLight, maxLight; - src->lightLevels(minLight, maxLight); - fprintf(file, " Sector levels { %g %g }\n", minLight, maxLight); - fprintf(file, "}\n"); - } + bsrc.lightLevels(minLight, maxLight); + + fprintf(file, "\n\nLight {"); + fprintf(file, "\n Map = \"%s\"", uid.toUtf8().constData()); + fprintf(file, "\n Origin { %g %g %g }", bsrc.origin().x, bsrc.origin().y, bsrc.origin().z); + fprintf(file, "\n Color { %g %g %g }", bsrc.color().x, bsrc.color().y, bsrc.color().z); + fprintf(file, "\n Intensity = %g", bsrc.intensity()); + fprintf(file, "\n Sector levels { %g %g }", minLight, maxLight); + fprintf(file, "\n}"); + + return LoopContinue; + }); fclose(file); Str_Free(&fileName); @@ -401,7 +401,7 @@ D_CMD(BLEditor) if(!qstricmp(cmd, "grab")) { - SBE_Grab(map.toIndex(*map.biasSourceNear(hand.origin()))); + SBE_Grab(map.indexOf(*map.biasSourceNear(hand.origin()))); return true; } @@ -427,11 +427,11 @@ D_CMD(BLEditor) int which = -1; if(!hand.isEmpty()) { - which = map.toIndex(hand.grabbed().first()->as()); + which = map.indexOf(hand.grabbed().first()->as()); } else { - which = map.toIndex(*map.biasSourceNear(hand.origin())); + which = map.indexOf(*map.biasSourceNear(hand.origin())); } if(argc > 1) @@ -453,7 +453,7 @@ D_CMD(BLEditor) if(!qstricmp(cmd, "dup")) { - return SBE_Dupe(*map.biasSource(which)) != 0; + return SBE_Dupe(map.biasSource(which)) != nullptr; } if(!qstricmp(cmd, "levels")) @@ -464,7 +464,7 @@ D_CMD(BLEditor) minLight = strtod(argv[1], 0) / 255.0f; maxLight = argc >= 3? strtod(argv[2], 0) / 255.0f : minLight; } - map.biasSource(which)->setLightLevels(minLight, maxLight); + map.biasSource(which).setLightLevels(minLight, maxLight); return true; } @@ -542,7 +542,7 @@ static void drawInfoBox(BiasSource *s, int rightX, String const title, float alp drawText(title, origin, UI_Color(UIC_TITLE), alpha); origin.y += th; - int sourceIndex = App_WorldSystem().map().toIndex(*s); + int sourceIndex = App_WorldSystem().map().indexOf(*s); coord_t distance = (s->origin() - vOrigin.xzy()).length(); float minLight, maxLight; s->lightLevels(minLight, maxLight); diff --git a/doomsday/client/src/mesh.cpp b/doomsday/client/src/mesh.cpp index 007e5b1e10..f0c33ac9e5 100644 --- a/doomsday/client/src/mesh.cpp +++ b/doomsday/client/src/mesh.cpp @@ -1,6 +1,6 @@ -/** @file mesh.cpp Mesh Geometry Data Structure. +/** @file mesh.cpp Mesh Geometry Data Structure. * - * @authors Copyright © 2008-2013 Daniel Swanson + * @authors Copyright © 2008-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -29,47 +29,36 @@ namespace de { DENG2_PIMPL_NOREF(Mesh::Element) { - /// Mesh owner of the element. - Mesh &mesh; - - /// MapElement to which the mesh element is attributed (if any). - MapElement *mapElement; - - Instance(Mesh &mesh) : mesh(mesh), mapElement(0) - {} + Mesh *mesh = nullptr; ///< Owner of the element. + MapElement *mapElement = nullptr; ///< Attributed MapElement if any (not owned). }; -Mesh::Element::Element(Mesh &mesh) : d(new Instance(mesh)) -{} +Mesh::Element::Element(Mesh &mesh) : d(new Instance) +{ + d->mesh = &mesh; +} Mesh &Mesh::Element::mesh() const { - return d->mesh; + DENG2_ASSERT(d->mesh); + return *d->mesh; } bool Mesh::Element::hasMapElement() const { - return d->mapElement != 0; + return d->mapElement != nullptr; } MapElement &Mesh::Element::mapElement() { - if(d->mapElement) - { - return *d->mapElement; - } + if(d->mapElement) return *d->mapElement; /// @throw MissingMapElement Attempted with no map element attributed. throw MissingMapElementError("Mesh::Element::mapElement", "No map element is attributed"); } MapElement const &Mesh::Element::mapElement() const { - if(d->mapElement) - { - return *d->mapElement; - } - /// @throw MissingMapElement Attempted with no map element attributed. - throw MissingMapElementError("Mesh::Element::mapElement", "No map element is attributed"); + return const_cast(this)->mapElement(); } void Mesh::Element::setMapElement(MapElement const *newMapElement) @@ -77,32 +66,24 @@ void Mesh::Element::setMapElement(MapElement const *newMapElement) d->mapElement = const_cast(newMapElement); } -DENG2_PIMPL(Mesh) +DENG2_PIMPL_NOREF(Mesh) { - /// All vertexes in the mesh. - Vertexes vertexes; - - /// All half-edges in the mesh. - HEdges hedges; - - /// All faces in the mesh. - Faces faces; - - Instance(Public *i) : Base(i) - {} - - ~Instance() - { - self.clear(); - } + Vertexs vertexs; ///< All vertexs in the mesh. + HEdges hedges; ///< All half-edges in the mesh. + Faces faces; ///< All faces in the mesh. }; -Mesh::Mesh() : d(new Instance(this)) +Mesh::Mesh() : d(new Instance) {} +Mesh::~Mesh() +{ + clear(); +} + void Mesh::clear() { - qDeleteAll(d->vertexes); d->vertexes.clear(); + qDeleteAll(d->vertexs); d->vertexs.clear(); qDeleteAll(d->hedges); d->hedges.clear(); qDeleteAll(d->faces); d->faces.clear(); } @@ -110,7 +91,7 @@ void Mesh::clear() Vertex *Mesh::newVertex(Vector2d const &origin) { Vertex *vtx = new Vertex(*this, origin); - d->vertexes.append(vtx); + d->vertexs.append(vtx); return vtx; } @@ -130,9 +111,9 @@ Face *Mesh::newFace() void Mesh::removeVertex(Vertex &vertex) { - int sizeBefore = d->vertexes.size(); - d->vertexes.removeOne(&vertex); - if(sizeBefore != d->vertexes.size()) + int sizeBefore = d->vertexs.size(); + d->vertexs.removeOne(&vertex); + if(sizeBefore != d->vertexs.size()) { delete &vertex; } @@ -158,9 +139,9 @@ void Mesh::removeFace(Face &face) } } -Mesh::Vertexes const &Mesh::vertexes() const +Mesh::Vertexs const &Mesh::vertexs() const { - return d->vertexes; + return d->vertexs; } Mesh::Faces const &Mesh::faces() const diff --git a/doomsday/client/src/render/biastracker.cpp b/doomsday/client/src/render/biastracker.cpp index 23add68435..616883a64e 100644 --- a/doomsday/client/src/render/biastracker.cpp +++ b/doomsday/client/src/render/biastracker.cpp @@ -238,7 +238,7 @@ void BiasTracker::applyChanges(BiasDigest &changes) /// sources by unique in-map index, and 2) re-index source references /// here upon deletion. The assumption being that affection changes /// occur far more frequently. - if(changes.isSourceChanged(App_WorldSystem().map().toIndex(*ctbr->source))) + if(changes.isSourceChanged(App_WorldSystem().map().indexOf(*ctbr->source))) { d->changedContributions |= 1 << i; } diff --git a/doomsday/client/src/render/blockmapvisual.cpp b/doomsday/client/src/render/blockmapvisual.cpp index b20a6a652e..88cc3b9fce 100644 --- a/doomsday/client/src/render/blockmapvisual.cpp +++ b/doomsday/client/src/render/blockmapvisual.cpp @@ -60,34 +60,12 @@ static void drawMobj(mobj_t const &mobj) glVertex2f(bounds.minX, bounds.maxY); } -static int drawMobjWorker(void *mobjPtr, void * /*context*/) -{ - mobj_t &mobj = *static_cast(mobjPtr); - if(mobj.validCount != validCount) - { - mobj.validCount = validCount; - drawMobj(mobj); - } - return false; // Continue iteration. -} - static void drawLine(Line const &line) { glVertex2f(line.fromOrigin().x, line.fromOrigin().y); glVertex2f( line.toOrigin().x, line.toOrigin().y); } -static int drawLineWorker(void *linePtr, void * /*context*/) -{ - Line &line = *static_cast(linePtr); - if(line.validCount() != validCount) - { - line.setValidCount(validCount); - drawLine(line); - } - return false; // Continue iteration. -} - static void drawSubspace(ConvexSubspace const &subspace) { float const scale = de::max(bmapDebugSize, 1.f); @@ -149,55 +127,72 @@ static void drawSubspace(ConvexSubspace const &subspace) } while((hedge = &hedge->next()) != base); } -static int drawSubspaceWorker(void *subspacePtr, void * /*context*/) -{ - ConvexSubspace &subspace = *static_cast(subspacePtr); - if(subspace.validCount() != validCount) - { - subspace.setValidCount(validCount); - drawSubspace(subspace); - } - return false; // Continue iteration. -} - -static int drawCellLines(Blockmap const &bmap, BlockmapCell const &cell, void *context) +static int drawCellLines(Blockmap const &bmap, BlockmapCell const &cell, void *) { glBegin(GL_LINES); - bmap.iterate(cell, (int (*)(void*,void*)) drawLineWorker, context); + bmap.forAllInCell(cell, [] (void *object) + { + Line &line = *(Line *)object; + if(line.validCount() != validCount) + { + line.setValidCount(validCount); + drawLine(line); + } + return LoopContinue; + }); glEnd(); return false; // Continue iteration. } -static int drawCellPolyobjLineWorker(void *object, void *context) -{ - Polyobj *po = (Polyobj *)object; - foreach(Line *line, po->lines()) - { - if(int result = drawLineWorker(line, context)) - return result; - } - return false; // Continue iteration. -} - static int drawCellPolyobjs(Blockmap const &bmap, BlockmapCell const &cell, void *context) { glBegin(GL_LINES); - bmap.iterate(cell, (int (*)(void*,void*)) drawCellPolyobjLineWorker, context); + bmap.forAllInCell(cell, [&context] (void *object) + { + Polyobj &pob = *(Polyobj *)object; + for(Line *line : pob.lines()) + { + if(line->validCount() != validCount) + { + line->setValidCount(validCount); + drawLine(*line); + } + } + return LoopContinue; + }); glEnd(); return false; // Continue iteration. } -static int drawCellMobjs(Blockmap const &bmap, BlockmapCell const &cell, void *context) +static int drawCellMobjs(Blockmap const &bmap, BlockmapCell const &cell, void *) { glBegin(GL_QUADS); - bmap.iterate(cell, (int (*)(void*,void*)) drawMobjWorker, context); + bmap.forAllInCell(cell, [] (void *object) + { + mobj_t &mob = *(mobj_t *)object; + if(mob.validCount != validCount) + { + mob.validCount = validCount; + drawMobj(mob); + } + return LoopContinue; + }); glEnd(); return false; // Continue iteration. } -static int drawCellSubspaces(Blockmap const &bmap, BlockmapCell const &cell, void *context) +static int drawCellSubspaces(Blockmap const &bmap, BlockmapCell const &cell, void *) { - bmap.iterate(cell, (int (*)(void*,void*)) drawSubspaceWorker, context); + bmap.forAllInCell(cell, [] (void *object) + { + ConvexSubspace *sub = (ConvexSubspace *)object; + if(sub->validCount() != validCount) + { + sub->setValidCount(validCount); + drawSubspace(*sub); + } + return LoopContinue; + }); return false; // Continue iteration. } diff --git a/doomsday/client/src/render/r_fakeradio.cpp b/doomsday/client/src/render/r_fakeradio.cpp index ec408b7b38..cc5d0f426d 100644 --- a/doomsday/client/src/render/r_fakeradio.cpp +++ b/doomsday/client/src/render/r_fakeradio.cpp @@ -28,6 +28,7 @@ #include "de_base.h" #include "de_render.h" +#include "world/blockmap.h" #include "world/lineowner.h" #include "world/map.h" #include "ConvexSubspace" @@ -193,16 +194,6 @@ void Rend_RadioUpdateVertexShadowOffsets(Vertex &vtx) } while(own != base); } -static int linkShadowLineToSubspaceWorker(ConvexSubspace *subspace, void *context) -{ - LineSide &side = *static_cast(context); - if(side.sectorPtr() == &subspace->sector()) - { - subspace->addShadowLine(side); - } - return false; // Continue iteration. -} - void Rend_RadioInitForMap(Map &map) { Time begunAt; @@ -212,10 +203,11 @@ void Rend_RadioInitForMap(Map &map) lineSideRadioData = reinterpret_cast( Z_Calloc(sizeof(*lineSideRadioData) * map.sideCount(), PU_MAP, 0)); - foreach(Vertex *vertex, map.vertexes()) + map.forAllVertexs([] (Vertex &vertex) { - Rend_RadioUpdateVertexShadowOffsets(*vertex); - } + Rend_RadioUpdateVertexShadowOffsets(vertex); + return LoopContinue; + }); /** * The algorithm: @@ -229,37 +221,61 @@ void Rend_RadioInitForMap(Map &map) * shadow edges cross one of the subspace's edges (not parallel), * link the line to the ConvexSubspace. */ - foreach(Line *line, map.lines()) + map.forAllLines([] (Line &line) { - if(!Rend_RadioLineCastsShadow(*line)) continue; - - // For each side of the line. - for(uint i = 0; i < 2; ++i) + if(Rend_RadioLineCastsShadow(line)) { - LineSide &side = line->side(i); - - if(!side.hasSector()) continue; - if(!side.hasSections()) continue; - - Vertex const &vtx0 = line->vertex(i); - Vertex const &vtx1 = line->vertex(i^1); - LineOwner const &vo0 = line->vertexOwner(i)->next(); - LineOwner const &vo1 = line->vertexOwner(i^1)->prev(); - - AABoxd bounds = line->aaBox(); - - // Use the extended points, they are wider than inoffsets. - Vector2d point = vtx0.origin() + vo0.extendedShadowOffset(); - V2d_AddToBoxXY(bounds.arvec2, point.x, point.y); - - point = vtx1.origin() + vo1.extendedShadowOffset(); - V2d_AddToBoxXY(bounds.arvec2, point.x, point.y); - - // Link the shadowing line to all the subspaces whose axis-aligned - // bounding box intersects 'bounds'. - map.subspaceBoxIterator(bounds, linkShadowLineToSubspaceWorker, &side); + // For each side of the line. + for(int i = 0; i < 2; ++i) + { + LineSide &side = line.side(i); + + if(!side.hasSector()) continue; + if(!side.hasSections()) continue; + + Vertex const &vtx0 = line.vertex(i); + Vertex const &vtx1 = line.vertex(i ^ 1); + LineOwner const &vo0 = line.vertexOwner(i)->next(); + LineOwner const &vo1 = line.vertexOwner(i ^ 1)->prev(); + + AABoxd box = line.aaBox(); + + // Use the extended points, they are wider than inoffsets. + Vector2d point = vtx0.origin() + vo0.extendedShadowOffset(); + V2d_AddToBoxXY(box.arvec2, point.x, point.y); + + point = vtx1.origin() + vo1.extendedShadowOffset(); + V2d_AddToBoxXY(box.arvec2, point.x, point.y); + + // Link the shadowing line to all the subspaces whose axis-aligned + // bounding box intersects 'bounds'. + validCount++; + int const localValidCount = validCount; + line.map().subspaceBlockmap().forAllInBox(box, [&box, &side, &localValidCount] (void *object) + { + ConvexSubspace &sub = *(ConvexSubspace *)object; + if(sub.validCount() != localValidCount) // not yet processed + { + sub.setValidCount(localValidCount); + if(&sub.sector() == side.sectorPtr()) + { + // Check the bounds. + AABoxd const &polyBox = sub.poly().aaBox(); + if(!(polyBox.maxX < box.minX || + polyBox.minX > box.maxX || + polyBox.minY > box.maxY || + polyBox.maxY < box.minY)) + { + sub.addShadowLine(side); + } + } + } + return LoopContinue; + }); + } } - } + return LoopContinue; + }); LOGDEV_GL_MSG("Completed in %.2f seconds") << begunAt.since(); } diff --git a/doomsday/client/src/render/r_things.cpp b/doomsday/client/src/render/r_things.cpp index 9a50338744..0ac22a00ca 100644 --- a/doomsday/client/src/render/r_things.cpp +++ b/doomsday/client/src/render/r_things.cpp @@ -530,16 +530,14 @@ void R_ProjectSprite(mobj_t *mo) ms.texture(MTU_PRIMARY).generalCase().analysisDataPointer(Texture::BrightPointAnalysis); DENG2_ASSERT(pl != 0); - Lumobj const *lum = cluster.sector().map().lumobj(mo->lumIdx); - DENG_ASSERT(lum != 0); - - vissprite_t *vis = R_NewVisSprite(VSPR_FLARE); + Lumobj const &lob = cluster.sector().map().lumobj(mo->lumIdx); + vissprite_t *vis = R_NewVisSprite(VSPR_FLARE); vis->pose.distance = distFromEye; // Determine the exact center of the flare. vis->pose.origin = moPos + visOff; - vis->pose.origin.z += lum->zOffset(); + vis->pose.origin.z += lob.zOffset(); float flareSize = pl->brightMul; // X offset to the flare position. @@ -564,7 +562,7 @@ void R_ProjectSprite(mobj_t *mo) vis->data.flare.size = 8; // Color is taken from the associated lumobj. - V3f_Set(vis->data.flare.color, lum->color().x, lum->color().y, lum->color().z); + V3f_Set(vis->data.flare.color, lob.color().x, lob.color().y, lob.color().z); vis->data.flare.factor = mo->haloFactors[viewPlayer - ddPlayers]; vis->data.flare.xOff = xOffset; diff --git a/doomsday/client/src/render/rend_fakeradio.cpp b/doomsday/client/src/render/rend_fakeradio.cpp index 01bcdd4e79..e8bdc4321f 100644 --- a/doomsday/client/src/render/rend_fakeradio.cpp +++ b/doomsday/client/src/render/rend_fakeradio.cpp @@ -1410,24 +1410,28 @@ void Rend_DrawShadowOffsetVerts() glEnable(GL_TEXTURE_2D); /// @todo fixme: Should use the visual plane heights of sector clusters. - foreach(Line *line, map.lines()) - for(uint k = 0; k < 2; ++k) + map.forAllLines([] (Line &line) { - Vertex &vtx = line->vertex(k); - LineOwner const *base = vtx.firstLineOwner(); - LineOwner const *own = base; - do + for(int i = 0; i < 2; ++i) { - Vector2d xy = vtx.origin() + own->extendedShadowOffset(); - coord_t z = own->line().frontSector().floor().heightSmoothed(); - drawPoint(Vector3d(xy.x, xy.y, z), 1, yellow); + Vertex &vtx = line.vertex(i); - xy = vtx.origin() + own->innerShadowOffset(); - drawPoint(Vector3d(xy.x, xy.y, z), 1, red); + LineOwner const *base = vtx.firstLineOwner(); + LineOwner const *own = base; + do + { + Vector2d xy = vtx.origin() + own->extendedShadowOffset(); + coord_t z = own->line().frontSector().floor().heightSmoothed(); + drawPoint(Vector3d(xy.x, xy.y, z), 1, yellow); - own = &own->next(); - } while(own != base); - } + xy = vtx.origin() + own->innerShadowOffset(); + drawPoint(Vector3d(xy.x, xy.y, z), 1, red); + + own = &own->next(); + } while(own != base); + } + return LoopContinue; + }); glDisable(GL_TEXTURE_2D); glDepthMask(GL_TRUE); diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index b69dcd8737..8fcd377b2d 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -40,6 +40,7 @@ #include "Texture" #include "Face" #include "world/map.h" +#include "world/blockmap.h" #include "world/lineowner.h" #include "world/p_object.h" #include "world/p_players.h" @@ -2596,21 +2597,21 @@ static void writeAllWallSections(HEdge *hedge) static void writeSubspaceWallSections() { - HEdge *base = curSubspace->poly().hedge(); + HEdge *base = curSubspace->poly().hedge(); HEdge *hedge = base; do { writeAllWallSections(hedge); } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, curSubspace->extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) + for(Mesh *mesh : curSubspace->extraMeshes()) + for(HEdge *hedge : mesh->hedges()) { writeAllWallSections(hedge); } - foreach(Polyobj *po, curSubspace->polyobjs()) - foreach(HEdge *hedge, po->mesh().hedges()) + for(Polyobj *po : curSubspace->polyobjs()) + for(HEdge *hedge : po->mesh().hedges()) { writeAllWallSections(hedge); } @@ -2997,13 +2998,13 @@ static void traverseBspTreeAndDrawSubspaces(Map::BspTree const *bspTree) static void generateDecorationFlares(Map &map) { Vector3d const viewPos = Rend_EyeOrigin().xzy(); - - foreach(Lumobj *lum, map.lumobjs()) + map.forAllLumobjs([&viewPos] (Lumobj &lob) { - lum->generateFlare(viewPos, R_ViewerLumobjDistance(lum->indexInMap())); + lob.generateFlare(viewPos, R_ViewerLumobjDistance(lob.indexInMap())); /// @todo mark these light sources visible for LensFx - } + return LoopContinue; + }); } /** @@ -3977,7 +3978,7 @@ static String labelForSource(BiasSource *s) { if(!s || !editShowIndices) return String(); /// @todo Don't assume the current map. - return String::number(App_WorldSystem().map().toIndex(*s)); + return String::number(App_WorldSystem().map().indexOf(*s)); } static void drawSource(BiasSource *s) @@ -4087,7 +4088,7 @@ static void drawBiasEditingVisuals(Map &map) if(nearSource->isLocked()) drawLock(nearSource->origin(), 2 + (nearSource->origin() - eyeOrigin).length() / 100, t); - foreach(Grabbable *grabbable, hand.grabbed()) + for(Grabbable *grabbable : hand.grabbed()) { if(de::internal::cannotCastGrabbableTo(grabbable)) continue; BiasSource *s = &grabbable->as(); @@ -4112,13 +4113,14 @@ static void drawBiasEditingVisuals(Map &map) // Show all sources? if(editShowAll) { - foreach(BiasSource *source, map.biasSources()) + map.forAllBiasSources([&nearSource] (BiasSource &source) { - if(source == nearSource) continue; - if(source->isGrabbed()) continue; - - drawSource(source); - } + if(&source != nearSource && !source.isGrabbed()) + { + drawSource(&source); + } + return LoopContinue; + }); } glEnable(GL_DEPTH_TEST); @@ -4464,15 +4466,16 @@ static void drawMobjBoundingBoxes(Map &map) if(devPolyobjBBox) { - foreach(Polyobj const *polyobj, map.polyobjs()) + map.forAllPolyobjs([] (Polyobj &pob) { - Sector const &sec = polyobj->sector(); - coord_t width = (polyobj->aaBox.maxX - polyobj->aaBox.minX)/2; - coord_t length = (polyobj->aaBox.maxY - polyobj->aaBox.minY)/2; + Sector const &sec = pob.sector(); + + coord_t width = (pob.aaBox.maxX - pob.aaBox.minX)/2; + coord_t length = (pob.aaBox.maxY - pob.aaBox.minY)/2; coord_t height = (sec.ceiling().height() - sec.floor().height())/2; - Vector3d pos(polyobj->aaBox.minX + width, - polyobj->aaBox.minY + length, + Vector3d pos(pob.aaBox.minX + width, + pob.aaBox.minY + length, sec.floor().height()); ddouble const distToEye = (eyeOrigin - pos).length(); @@ -4482,7 +4485,7 @@ static void drawMobjBoundingBoxes(Map &map) Rend_DrawBBox(pos, width, length, height, 0, yellow, alpha, .08f); - foreach(Line *line, polyobj->lines()) + for(Line *line : pob.lines()) { Vector3d pos(line->center(), sec.floor().height()); @@ -4490,7 +4493,9 @@ static void drawMobjBoundingBoxes(Map &map) BANG2DEG(BANG_90 - line->angle()), green, alpha, 0); } - } + + return LoopContinue; + }); } GL_BlendMode(BM_NORMAL); @@ -4675,47 +4680,46 @@ static void drawLumobjs(Map &map) glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); - for(int i = 0; i < map.lumobjCount(); ++i) + map.forAllLumobjs([] (Lumobj &lob) { - Lumobj *lum = map.lumobj(i); - - if(rendMaxLumobjs > 0 && R_ViewerLumobjIsHidden(i)) - continue; + if(rendMaxLumobjs > 0 && R_ViewerLumobjIsHidden(lob.indexInMap())) + return LoopContinue; glMatrixMode(GL_MODELVIEW); glPushMatrix(); - glTranslated(lum->origin().x, lum->origin().z + lum->zOffset(), lum->origin().y); + glTranslated(lob.origin().x, lob.origin().z + lob.zOffset(), lob.origin().y); glBegin(GL_LINES); { glColor4fv(black); - glVertex3f(-lum->radius(), 0, 0); - glColor4f(lum->color().x, lum->color().y, lum->color().z, 1); + glVertex3f(-lob.radius(), 0, 0); + glColor4f(lob.color().x, lob.color().y, lob.color().z, 1); glVertex3f(0, 0, 0); glVertex3f(0, 0, 0); glColor4fv(black); - glVertex3f(lum->radius(), 0, 0); + glVertex3f(lob.radius(), 0, 0); - glVertex3f(0, -lum->radius(), 0); - glColor4f(lum->color().x, lum->color().y, lum->color().z, 1); + glVertex3f(0, -lob.radius(), 0); + glColor4f(lob.color().x, lob.color().y, lob.color().z, 1); glVertex3f(0, 0, 0); glVertex3f(0, 0, 0); glColor4fv(black); - glVertex3f(0, lum->radius(), 0); + glVertex3f(0, lob.radius(), 0); - glVertex3f(0, 0, -lum->radius()); - glColor4f(lum->color().x, lum->color().y, lum->color().z, 1); + glVertex3f(0, 0, -lob.radius()); + glColor4f(lob.color().x, lob.color().y, lob.color().z, 1); glVertex3f(0, 0, 0); glVertex3f(0, 0, 0); glColor4fv(black); - glVertex3f(0, 0, lum->radius()); + glVertex3f(0, 0, lob.radius()); } glEnd(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); - } + return LoopContinue; + }); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); @@ -4746,50 +4750,54 @@ static void drawSoundEmitters(Map &map) if(devSoundEmitters & SOF_SIDE) { - foreach(Line *line, map.lines()) - for(int i = 0; i < 2; ++i) + map.forAllLines([] (Line &line) { - LineSide &side = line->side(i); - if(!side.hasSections()) continue; - - drawSoundEmitter(side.middleSoundEmitter(), - String("Line #%1 (%2, middle)") - .arg(line->indexInMap()) - .arg(i? "back" : "front")); - - drawSoundEmitter(side.bottomSoundEmitter(), - String("Line #%1 (%2, bottom)") - .arg(line->indexInMap()) - .arg(i? "back" : "front")); - - drawSoundEmitter(side.topSoundEmitter(), - String("Line #%1 (%2, top)") - .arg(line->indexInMap()) - .arg(i? "back" : "front")); - } + for(int i = 0; i < 2; ++i) + { + LineSide &side = line.side(i); + if(!side.hasSections()) continue; + + drawSoundEmitter(side.middleSoundEmitter(), + String("Line #%1 (%2, middle)") + .arg(line.indexInMap()) + .arg(i? "back" : "front")); + + drawSoundEmitter(side.bottomSoundEmitter(), + String("Line #%1 (%2, bottom)") + .arg(line.indexInMap()) + .arg(i? "back" : "front")); + + drawSoundEmitter(side.topSoundEmitter(), + String("Line #%1 (%2, top)") + .arg(line.indexInMap()) + .arg(i? "back" : "front")); + } + return LoopContinue; + }); } if(devSoundEmitters & (SOF_SECTOR|SOF_PLANE)) { - foreach(Sector *sec, map.sectors()) + map.forAllSectors([] (Sector &sec) { if(devSoundEmitters & SOF_PLANE) { - foreach(Plane *plane, sec->planes()) + for(Plane *plane : sec.planes()) { drawSoundEmitter(plane->soundEmitter(), String("Sector #%1 (pln:%2)") - .arg(sec->indexInMap()) + .arg(sec.indexInMap()) .arg(plane->indexInSector())); } } if(devSoundEmitters & SOF_SECTOR) { - drawSoundEmitter(sec->soundEmitter(), - String("Sector #%1").arg(sec->indexInMap())); + drawSoundEmitter(sec.soundEmitter(), + String("Sector #%1").arg(sec.indexInMap())); } - } + return LoopContinue; + }); } } @@ -4958,16 +4966,13 @@ static void findMinMaxPlaneHeightsAtVertex(HEdge *base, int edge, } } -static int drawSubspaceVertexWorker(ConvexSubspace *subspace, void *context) +static void drawSubspaceVertexs(ConvexSubspace &sub, drawVertexVisual_params_t &parms) { - drawVertexVisual_params_t &parms = *static_cast(context); - - SectorCluster &cluster = subspace->cluster(); + SectorCluster &cluster = sub.cluster(); + ddouble const min = cluster. visFloor().heightSmoothed(); + ddouble const max = cluster.visCeiling().heightSmoothed(); - ddouble min = cluster. visFloor().heightSmoothed(); - ddouble max = cluster.visCeiling().heightSmoothed(); - - HEdge *base = subspace->poly().hedge(); + HEdge *base = sub.poly().hedge(); HEdge *hedge = base; do { @@ -4979,21 +4984,19 @@ static int drawSubspaceVertexWorker(ConvexSubspace *subspace, void *context) } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, subspace->extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) + for(Mesh *mesh : sub.extraMeshes()) + for(HEdge *hedge : mesh->hedges()) { drawVertexVisual(hedge->vertex(), min, max, parms); drawVertexVisual(hedge->twin().vertex(), min, max, parms); } - foreach(Polyobj *polyobj, subspace->polyobjs()) - foreach(Line *line, polyobj->lines()) + for(Polyobj *polyobj : sub.polyobjs()) + for(Line *line : polyobj->lines()) { drawVertexVisual(line->from(), min, max, parms); drawVertexVisual(line->to(), min, max, parms); } - - return false; // Continue iteration. } /** @@ -5024,9 +5027,23 @@ static void drawVertexes(Map &map) oldLineWidth = DGL_GetFloat(DGL_LINE_WIDTH); DGL_SetFloat(DGL_LINE_WIDTH, 2); - parms.drawBar = true; + parms.drawBar = true; parms.drawLabel = parms.drawOrigin = false; - map.subspaceBoxIterator(box, drawSubspaceVertexWorker, &parms); + + map.subspaceBlockmap().forAllInBox(box, [&box, &parms] (void *object) + { + ConvexSubspace &sub = *(ConvexSubspace *)object; + // Check the bounds. + AABoxd const &polyBox = sub.poly().aaBox(); + if(!(polyBox.maxX < box.minX || + polyBox.minX > box.maxX || + polyBox.minY > box.maxY || + polyBox.maxY < box.minY)) + { + drawSubspaceVertexs(sub, parms); + } + return LoopContinue; + }); glEnable(GL_DEPTH_TEST); } @@ -5041,8 +5058,21 @@ static void drawVertexes(Map &map) parms.drawnVerts->fill(false); // Process all again. parms.drawOrigin = true; - parms.drawBar = parms.drawLabel = false; - map.subspaceBoxIterator(box, drawSubspaceVertexWorker, &parms); + parms.drawBar = parms.drawLabel = false; + map.subspaceBlockmap().forAllInBox(box, [&box, &parms] (void *object) + { + ConvexSubspace &sub = *(ConvexSubspace *)object; + // Check the bounds. + AABoxd const &polyBox = sub.poly().aaBox(); + if(!(polyBox.maxX < box.minX || + polyBox.minX > box.maxX || + polyBox.minY > box.maxY || + polyBox.maxY < box.minY)) + { + drawSubspaceVertexs(sub, parms); + } + return LoopContinue; + }); glEnable(GL_DEPTH_TEST); @@ -5050,8 +5080,21 @@ static void drawVertexes(Map &map) { parms.drawnVerts->fill(false); // Process all again. parms.drawLabel = true; - parms.drawBar = parms.drawOrigin = false; - map.subspaceBoxIterator(box, drawSubspaceVertexWorker, &parms); + parms.drawBar = parms.drawOrigin = false; + map.subspaceBlockmap().forAllInBox(box, [&box, &parms] (void *object) + { + ConvexSubspace &sub = *(ConvexSubspace *)object; + // Check the bounds. + AABoxd const &polyBox = sub.poly().aaBox(); + if(!(polyBox.maxX < box.minX || + polyBox.minX > box.maxX || + polyBox.minY > box.maxY || + polyBox.maxY < box.minY)) + { + drawSubspaceVertexs(sub, parms); + } + return LoopContinue; + }); } // Restore previous state. diff --git a/doomsday/client/src/render/surfacedecorator.cpp b/doomsday/client/src/render/surfacedecorator.cpp index 69c43420d1..bb6aa1494a 100644 --- a/doomsday/client/src/render/surfacedecorator.cpp +++ b/doomsday/client/src/render/surfacedecorator.cpp @@ -19,9 +19,13 @@ * 02110-1301 USA */ -#include "de_platform.h" #include "render/surfacedecorator.h" +#include +#include +#include +#include + #include "world/map.h" #include "BspLeaf" #include "ConvexSubspace" @@ -33,29 +37,21 @@ #include "LightDecoration" #include "WallEdge" -#include -#include -#include -#include - using namespace de; typedef QSet SurfaceSet; typedef QMap MaterialSurfaceMap; -DENG2_PIMPL(SurfaceDecorator), -DENG2_OBSERVES(Material, DimensionsChange), -DENG2_OBSERVES(MaterialAnimation, DecorationStageChange) +DENG2_PIMPL_NOREF(SurfaceDecorator) +, DENG2_OBSERVES(Material, DimensionsChange) +, DENG2_OBSERVES(MaterialAnimation, DecorationStageChange) { MaterialSurfaceMap decorated; ///< All surfaces being looked after. - Instance(Public *i) : Base(i) - {} - ~Instance() { - foreach(SurfaceSet const &set, decorated) - foreach(Surface *surface, set) + for(SurfaceSet const &set : decorated) + for(Surface *surface : set) { observeMaterial(surface->material(), false); } @@ -77,7 +73,7 @@ DENG2_OBSERVES(MaterialAnimation, DecorationStageChange) void updateDecorations(Surface &suf, MaterialSnapshot const &materialSnapshot, Vector2f const &materialOrigin, Vector3d const &topLeft, - Vector3d const &bottomRight, Sector *containingSector = 0) + Vector3d const &bottomRight, Sector *containingSector = nullptr) { Vector3d delta = bottomRight - topLeft; if(de::fequal(delta.length(), 0)) return; @@ -156,9 +152,9 @@ DENG2_OBSERVES(MaterialAnimation, DecorationStageChange) { MaterialSurfaceMap::const_iterator found = decorated.constFind(&material); if(found != decorated.constEnd()) - foreach(Surface *surface, found.value()) + for(Surface *surface : found.value()) { - surface->markAsNeedingDecorationUpdate(); + surface->markForDecorationUpdate(); } } @@ -169,15 +165,14 @@ DENG2_OBSERVES(MaterialAnimation, DecorationStageChange) } /// Observes MaterialAnimation DecorationStageChange - void materialAnimationDecorationStageChanged(MaterialAnimation &anim, + void materialAnimationDecorationStageChanged(MaterialAnimation & /*anim*/, Material::Decoration &decor) { markSurfacesForRedecoration(decor.material()); - DENG2_UNUSED(anim); } }; -SurfaceDecorator::SurfaceDecorator() : d(new Instance(this)) +SurfaceDecorator::SurfaceDecorator() : d(new Instance) {} static bool prepareGeometry(Surface &surface, Vector3d &topLeft, @@ -246,10 +241,10 @@ void SurfaceDecorator::decorate(Surface &surface) if(!surface.hasMaterial()) return; // Huh? - if(!surface._needDecorationUpdate) + if(!surface.needsDecorationUpdate()) return; - surface._needDecorationUpdate = false; + surface.markForDecorationUpdate(false); surface.clearDecorations(); Vector3d topLeft, bottomRight; @@ -273,9 +268,9 @@ void SurfaceDecorator::redecorate() MaterialSnapshot const *materialSnapshot = 0; SurfaceSet const &surfaceSet = i.value(); - foreach(Surface *surface, surfaceSet) + for(Surface *surface : surfaceSet) { - if(!surface->_needDecorationUpdate) + if(!surface->needsDecorationUpdate()) continue; // Time to prepare the material? @@ -285,7 +280,7 @@ void SurfaceDecorator::redecorate() materialSnapshot = &material.prepare(Rend_MapSurfaceMaterialSpec()); } - surface->_needDecorationUpdate = false; + surface->markForDecorationUpdate(false); surface->clearDecorations(); Vector3d topLeft, bottomRight; diff --git a/doomsday/client/src/render/viewports.cpp b/doomsday/client/src/render/viewports.cpp index b165de5d9f..a78f37fa6a 100644 --- a/doomsday/client/src/render/viewports.cpp +++ b/doomsday/client/src/render/viewports.cpp @@ -1275,12 +1275,13 @@ void R_BeginFrame() // Update viewer => lumobj distances ready for linking and sorting. viewdata_t const *viewData = R_ViewData(viewPlayer - ddPlayers); - foreach(Lumobj *lum, map.lumobjs()) + map.forAllLumobjs([&viewData] (Lumobj &lob) { // Approximate the distance in 3D. - Vector3d delta = lum->origin() - viewData->current.origin; - luminousDist[lum->indexInMap()] = M_ApproxDistance3(delta.x, delta.y, delta.z * 1.2 /*correct aspect*/); - } + Vector3d delta = lob.origin() - viewData->current.origin; + luminousDist[lob.indexInMap()] = M_ApproxDistance3(delta.x, delta.y, delta.z * 1.2 /*correct aspect*/); + return LoopContinue; + }); if(rendMaxLumobjs > 0 && numLuminous > rendMaxLumobjs) { @@ -1363,8 +1364,8 @@ void R_ViewerClipLumobjBySight(Lumobj *lum, ConvexSubspace *subspace) // between the viewpoint and the lumobj. Vector3d const eye = Rend_EyeOrigin().xzy(); - foreach(Polyobj *po, subspace->polyobjs()) - foreach(HEdge *hedge, po->mesh().hedges()) + for(Polyobj *po : subspace->polyobjs()) + for(HEdge *hedge : po->mesh().hedges()) { // Is this on the back of a one-sided line? if(!hedge->hasMapElement()) diff --git a/doomsday/client/src/resource/resourcesystem.cpp b/doomsday/client/src/resource/resourcesystem.cpp index 90bf11c644..f0b9fd733e 100644 --- a/doomsday/client/src/resource/resourcesystem.cpp +++ b/doomsday/client/src/resource/resourcesystem.cpp @@ -3951,33 +3951,38 @@ void ResourceSystem::cacheForCurrentMap() { MaterialVariantSpec const &spec = Rend_MapSurfaceMaterialSpec(); - foreach(Line *line, map.lines()) - for(int i = 0; i < 2; ++i) + map.forAllLines([this, &spec] (Line &line) { - LineSide &side = line->side(i); - if(!side.hasSections()) continue; + for(int i = 0; i < 2; ++i) + { + LineSide &side = line.side(i); + if(!side.hasSections()) continue; - if(side.middle().hasMaterial()) - cache(side.middle().material(), spec); + if(side.middle().hasMaterial()) + cache(side.middle().material(), spec); - if(side.top().hasMaterial()) - cache(side.top().material(), spec); + if(side.top().hasMaterial()) + cache(side.top().material(), spec); - if(side.bottom().hasMaterial()) - cache(side.bottom().material(), spec); - } + if(side.bottom().hasMaterial()) + cache(side.bottom().material(), spec); + } + return LoopContinue; + }); - foreach(Sector *sector, map.sectors()) + map.forAllSectors([this, &spec] (Sector §or) { // Skip sectors with no line sides as their planes will never be drawn. - if(!sector->sideCount()) continue; - - foreach(Plane *plane, sector->planes()) + if(sector.sideCount()) + for(Plane *plane : sector.planes()) { if(plane->surface().hasMaterial()) + { cache(plane->surface().material(), spec); + } } - } + return LoopContinue; + }); } if(precacheSprites) diff --git a/doomsday/client/src/ui/inputsystem.cpp b/doomsday/client/src/ui/inputsystem.cpp index 30d0442a63..485af2ca0b 100644 --- a/doomsday/client/src/ui/inputsystem.cpp +++ b/doomsday/client/src/ui/inputsystem.cpp @@ -774,8 +774,6 @@ DENG2_PIMPL(InputSystem) */ void updateAllDeviceStateAssociations() { - auto &_self = self; - // Clear all existing associations. for(InputDevice *device : devices) { @@ -793,16 +791,16 @@ DENG2_PIMPL(InputSystem) if(!context->isActive()) continue; - context->forAllCommandBindings([&_self, &context] (Record &rec) + context->forAllCommandBindings([this, &context] (Record &rec) { CommandBinding bind(rec); InputDeviceControl *ctrl = nullptr; switch(bind.geti("type")) { - case E_AXIS: ctrl = &_self.device(bind.geti("deviceId")).axis (bind.geti("controlId")); break; - case E_TOGGLE: ctrl = &_self.device(bind.geti("deviceId")).button(bind.geti("controlId")); break; - case E_ANGLE: ctrl = &_self.device(bind.geti("deviceId")).hat (bind.geti("controlId")); break; + case E_AXIS: ctrl = &self.device(bind.geti("deviceId")).axis (bind.geti("controlId")); break; + case E_TOGGLE: ctrl = &self.device(bind.geti("deviceId")).button(bind.geti("controlId")); break; + case E_ANGLE: ctrl = &self.device(bind.geti("deviceId")).hat (bind.geti("controlId")); break; case E_SYMBOLIC: break; @@ -820,10 +818,10 @@ DENG2_PIMPL(InputSystem) }); // Associate all the device bindings. - context->forAllImpulseBindings([&_self, &context] (Record &rec) + context->forAllImpulseBindings([this, &context] (Record &rec) { ImpulseBinding bind(rec); - InputDevice &dev = _self.device(bind.geti("deviceId")); + InputDevice &dev = self.device(bind.geti("deviceId")); InputDeviceControl *ctrl = nullptr; switch(bind.geti("type")) diff --git a/doomsday/client/src/world/api_map.cpp b/doomsday/client/src/world/api_map.cpp index 808e51ed78..17269b47e5 100644 --- a/doomsday/client/src/world/api_map.cpp +++ b/doomsday/client/src/world/api_map.cpp @@ -1,10 +1,10 @@ -/** @file api_map.cpp Doomsday Map Update API. +/** @file api_map.cpp Doomsday Map Update API. * * @todo Throw a game-terminating exception if an illegal value is given * to a public API function. * - * @authors Copyright © 2006-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Jaakko Keränen + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -38,6 +38,7 @@ #include "Face" +#include "world/blockmap.h" #include "world/dmuargs.h" #include "world/entitydatabase.h" #include "world/maputil.h" @@ -228,9 +229,9 @@ void *P_AllocDummy(int type, void *extraData) { case DMU_LINE: { // Time to allocate the dummy vertex? - if(dummyMesh.vertexesIsEmpty()) + if(dummyMesh.vertexsIsEmpty()) dummyMesh.newVertex(); - Vertex &dummyVertex = *dummyMesh.vertexes().first(); + Vertex &dummyVertex = *dummyMesh.vertexs().first(); DummyLine *dl = new DummyLine(dummyVertex, dummyVertex); dummies.insert(dl); @@ -325,18 +326,16 @@ void *P_ToPtr(int type, int index) switch(type) { case DMU_VERTEX: - return App_WorldSystem().map().vertexes().at(index); + return App_WorldSystem().map().vertexPtr(index); case DMU_LINE: - return App_WorldSystem().map().lines().at(index); + return App_WorldSystem().map().linePtr(index); case DMU_SIDE: - return App_WorldSystem().map().sideByIndex(index); + return App_WorldSystem().map().sidePtr(index); case DMU_SECTOR: - if(index < 0 || index >= App_WorldSystem().map().sectors().size()) - return 0; - return App_WorldSystem().map().sectors().at(index); + return App_WorldSystem().map().sectorPtr(index); case DMU_PLANE: { /// @todo Throw exception. @@ -345,7 +344,7 @@ void *P_ToPtr(int type, int index) return 0; /* Unreachable. */ } case DMU_SUBSPACE: - return App_WorldSystem().map().subspaces().at(index); + return App_WorldSystem().map().subspacePtr(index); case DMU_SKY: if(index != 0) return 0; // Only one sky per map, presently. @@ -462,29 +461,38 @@ int P_Callback(int type, int index, int (*callback)(void *p, void *ctx), void *c switch(type) { case DMU_VERTEX: - if(index >= 0 && index < App_WorldSystem().map().vertexCount()) - return callback(App_WorldSystem().map().vertexes().at(index), context); + if(Vertex *vtx = App_WorldSystem().map().vertexPtr(index)) + { + return callback(vtx, context); + } break; case DMU_LINE: - if(index >= 0 && index < App_WorldSystem().map().lineCount()) - return callback(App_WorldSystem().map().lines().at(index), context); + if(Line *li = App_WorldSystem().map().linePtr(index)) + { + return callback(li, context); + } break; - case DMU_SIDE: { - LineSide *side = App_WorldSystem().map().sideByIndex(index); - if(side) - return callback(side, context); - break; } + case DMU_SIDE: + if(LineSide *si = App_WorldSystem().map().sidePtr(index)) + { + return callback(si, context); + } + break; case DMU_SUBSPACE: - if(index >= 0 && index < App_WorldSystem().map().subspaceCount()) - return callback(App_WorldSystem().map().subspaces().at(index), context); + if(ConvexSubspace *sub = App_WorldSystem().map().subspacePtr(index)) + { + return callback(sub, context); + } break; case DMU_SECTOR: - if(index >= 0 && index < App_WorldSystem().map().sectorCount()) - return callback(App_WorldSystem().map().sectors().at(index), context); + if(Sector *sec = App_WorldSystem().map().sectorPtr(index)) + { + return callback(sec, context); + } break; case DMU_PLANE: { @@ -1559,28 +1567,28 @@ DENG_EXTERN_C void Mobj_Unlink(mobj_t *mobj) DENG_EXTERN_C int Mobj_TouchedLinesIterator(mobj_t *mo, int (*callback) (Line *, void *), void *context) { if(!mo || !Mobj_IsLinked(*mo)) return false; // Continue iteration. - return Mobj_Map(*mo).mobjTouchedLineIterator(mo, callback, context); + return Mobj_Map(*mo).forAllLinesTouchingMobj(mo, callback, context); } #undef Mobj_TouchedSectorsIterator DENG_EXTERN_C int Mobj_TouchedSectorsIterator(mobj_t *mo, int (*callback) (Sector *, void *), void *context) { if(!mo || !Mobj_IsLinked(*mo)) return false; // Continue iteration. - return Mobj_Map(*mo).mobjTouchedSectorIterator(mo, callback, context); + return Mobj_Map(*mo).forAllSectorsTouchingMobj(mo, callback, context); } #undef Line_TouchingMobjsIterator DENG_EXTERN_C int Line_TouchingMobjsIterator(Line *line, int (*callback) (mobj_t *, void *), void *context) { if(!line) return false; // Continue iteration. - return line->map().lineTouchingMobjIterator(line, callback, context); + return line->map().forAllMobjsTouchingLine(line, callback, context); } #undef Sector_TouchingMobjsIterator DENG_EXTERN_C int Sector_TouchingMobjsIterator(Sector *sector, int (*callback) (mobj_t *, void *), void *context) { if(!sector) return false; // Continue iteration. - return sector->map().sectorTouchingMobjIterator(sector, callback, context); + return sector->map().forAllMobjsTouchingSector(sector, callback, context); } #undef Sector_AtPoint_FixedPrecision @@ -1594,32 +1602,104 @@ DENG_EXTERN_C Sector *Sector_AtPoint_FixedPrecision(const_pvec2d_t point) DENG_EXTERN_C int Mobj_BoxIterator(AABoxd const *box, int (*callback) (mobj_t *, void *), void *context) { - if(!box || !App_WorldSystem().hasMap()) return false; // Continue iteration. - return App_WorldSystem().map().mobjBoxIterator(*box, callback, context); + DENG2_ASSERT(box && callback); + + LoopResult result = LoopContinue; + if(App_WorldSystem().hasMap()) + { + Map const &map = App_WorldSystem().map(); + int const localValidCount = validCount; + + result = map.mobjBlockmap().forAllInBox(*box, [&callback, &context, &localValidCount] (void *object) + { + mobj_t &mob = *(mobj_t *)object; + if(mob.validCount != localValidCount) // not yet processed + { + mob.validCount = localValidCount; + return LoopResult( callback(&mob, context) ); + } + return LoopResult(); // continue + }); + } + return result; } #undef Polyobj_BoxIterator DENG_EXTERN_C int Polyobj_BoxIterator(AABoxd const *box, int (*callback) (struct polyobj_s *, void *), void *context) { - if(!box || !App_WorldSystem().hasMap()) return false; // Continue iteration. - return App_WorldSystem().map().polyobjBoxIterator(*box, callback, context); + DENG2_ASSERT(box && callback); + + LoopResult result = LoopContinue; + if(App_WorldSystem().hasMap()) + { + Map const &map = App_WorldSystem().map(); + int const localValidCount = validCount; + + result = map.polyobjBlockmap().forAllInBox(*box, [&callback, &context, &localValidCount] (void *object) + { + Polyobj &pob = *(Polyobj *)object; + if(pob.validCount != localValidCount) // not yet processed + { + pob.validCount = localValidCount; + return LoopResult( callback(&pob, context) ); + } + return LoopResult(); // continue + }); + } + return result; } #undef Line_BoxIterator DENG_EXTERN_C int Line_BoxIterator(AABoxd const *box, int flags, int (*callback) (Line *, void *), void *context) { - if(!box || !App_WorldSystem().hasMap()) return false; // Continue iteration. - return App_WorldSystem().map().lineBoxIterator(*box, flags, callback, context); + DENG2_ASSERT(box && callback); + + LoopResult result = LoopContinue; + if(App_WorldSystem().hasMap()) + { + Map const &map = App_WorldSystem().map(); + result = map.forAllLinesInBox(*box, flags, [&callback, &context] (Line &line) + { + return LoopResult( callback(&line, context) ); + }); + } + return result; } #undef Subspace_BoxIterator DENG_EXTERN_C int Subspace_BoxIterator(AABoxd const *box, int (*callback) (ConvexSubspace *, void *), void *context) { - if(!box || !App_WorldSystem().hasMap()) return false; // Continue iteration. - return App_WorldSystem().map().subspaceBoxIterator(*box, callback, context); + DENG2_ASSERT(box && callback); + + LoopResult result = LoopContinue; + if(App_WorldSystem().hasMap()) + { + Map const &map = App_WorldSystem().map(); + int const localValidCount = validCount; + + result = map.subspaceBlockmap().forAllInBox(*box, [&box, &callback, &context, &localValidCount] (void *object) + { + ConvexSubspace &sub = *(ConvexSubspace *)object; + if(sub.validCount() != localValidCount) // not yet processed + { + sub.setValidCount(localValidCount); + // Check the bounds. + AABoxd const &polyBox = sub.poly().aaBox(); + if(!(polyBox.maxX < box->minX || + polyBox.minX > box->maxX || + polyBox.minY > box->maxY || + polyBox.maxY < box->minY)) + { + return LoopResult( callback(&sub, context) ); + } + } + return LoopResult(); // continue + }); + } + return result; } #undef P_PathTraverse2 @@ -1727,15 +1807,27 @@ DENG_EXTERN_C void Polyobj_Link(Polyobj *po) #undef Polyobj_ById DENG_EXTERN_C Polyobj *Polyobj_ById(int index) { - if(!App_WorldSystem().hasMap()) return 0; - return App_WorldSystem().map().polyobjs().at(index); + if(!App_WorldSystem().hasMap()) return nullptr; + return App_WorldSystem().map().polyobjPtr(index); } #undef Polyobj_ByTag DENG_EXTERN_C Polyobj *Polyobj_ByTag(int tag) { - if(!App_WorldSystem().hasMap()) return 0; - return App_WorldSystem().map().polyobjByTag(tag); + Polyobj *found = nullptr; // not found. + if(App_WorldSystem().hasMap()) + { + App_WorldSystem().map().forAllPolyobjs([&tag, &found] (Polyobj &pob) + { + if(pob.tag == tag) + { + found = &pob; + return LoopAbort; + } + return LoopContinue; + }); + } + return found; } #undef Polyobj_Move diff --git a/doomsday/client/src/world/api_mapedit.cpp b/doomsday/client/src/world/api_mapedit.cpp index 283eac30fe..e54d1e61cc 100644 --- a/doomsday/client/src/world/api_mapedit.cpp +++ b/doomsday/client/src/world/api_mapedit.cpp @@ -273,15 +273,15 @@ int MPE_LineCreate(int v1, int v2, int frontSectorIdx, int backSectorIdx, int fl // Next, check the length is not zero. /// @todo fixme: We need to allow these... -ds - Vertex *vtx1 = editMap->vertexes().at(v1); - Vertex *vtx2 = editMap->vertexes().at(v2); - if(de::abs(Vector2d(vtx1->origin() - vtx2->origin()).length()) <= 0.0001) return -1; + Vertex &vtx1 = editMap->vertex(v1); + Vertex &vtx2 = editMap->vertex(v2); + if(de::abs(Vector2d(vtx1.origin() - vtx2.origin()).length()) <= 0.0001) return -1; Sector *frontSector = (frontSectorIdx >= 0? editMap->editableSectors().at(frontSectorIdx) : 0); Sector *backSector = (backSectorIdx >= 0? editMap->editableSectors().at(backSectorIdx) : 0); - return editMap->createLine(*vtx1, *vtx2, flags, frontSector, backSector, - archiveIndex)->indexInMap(); + return editMap->createLine(vtx1, vtx2, flags, frontSector, backSector, archiveIndex) + ->indexInMap(); } #undef MPE_LineAddSide diff --git a/doomsday/client/src/world/blockmap.cpp b/doomsday/client/src/world/blockmap.cpp index e5aaa9e36e..214fc2e283 100644 --- a/doomsday/client/src/world/blockmap.cpp +++ b/doomsday/client/src/world/blockmap.cpp @@ -1,7 +1,7 @@ -/** @file blockmap.cpp World map element blockmap. +/** @file blockmap.cpp World map element blockmap. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * @authors Copyright © 1993-1996 by id Software, Inc. * * @par License @@ -19,11 +19,11 @@ * 02110-1301 USA */ -#include +#include "world/blockmap.h" +#include #include #include - #include #include "de_base.h" @@ -31,8 +31,6 @@ #include "de_graphics.h" // For debug visual. #include "de_render.h" // For debug visual. -#include "world/blockmap.h" - namespace de { struct RingNode @@ -442,7 +440,7 @@ bool Blockmap::link(Cell const &cell, void *elem) { if(!elem) return false; // Huh? - if(CellData *cellData = d->cellData(cell, true /*can create*/)) + if(auto *cellData = d->cellData(cell, true /*can create*/)) { return cellData->link(elem); } @@ -462,7 +460,7 @@ bool Blockmap::link(AABoxd const ®ion, void *elem) for(cell.y = cellBlock.min.y; cell.y < cellBlock.max.y; ++cell.y) for(cell.x = cellBlock.min.x; cell.x < cellBlock.max.x; ++cell.x) { - if(CellData *cellData = d->cellData(cell, true)) + if(auto *cellData = d->cellData(cell, true)) { if(cellData->link(elem)) { @@ -478,7 +476,7 @@ bool Blockmap::unlink(Cell const &cell, void *elem) { if(!elem) return false; // Huh? - if(CellData *cellData = d->cellData(cell)) + if(auto *cellData = d->cellData(cell)) { return cellData->unlink(elem); } @@ -498,7 +496,7 @@ bool Blockmap::unlink(AABoxd const ®ion, void *elem) for(cell.y = cellBlock.min.y; cell.y < cellBlock.max.y; ++cell.y) for(cell.x = cellBlock.min.x; cell.x < cellBlock.max.x; ++cell.x) { - if(CellData *cellData = d->cellData(cell)) + if(auto *cellData = d->cellData(cell)) { if(cellData->unlink(elem)) { @@ -512,12 +510,12 @@ bool Blockmap::unlink(AABoxd const ®ion, void *elem) void Blockmap::unlinkAll() { - foreach(Instance::Node const &node, d->nodes) + for(Instance::Node const &node : d->nodes) { // Only leafs with user data. if(!node.isLeaf()) continue; - if(CellData *cellData = node.leafData) + if(auto *cellData = node.leafData) { cellData->unlinkAll(); } @@ -526,58 +524,47 @@ void Blockmap::unlinkAll() int Blockmap::cellElementCount(Cell const &cell) const { - if(CellData *cellData = d->cellData(cell)) + if(auto *cellData = d->cellData(cell)) { return cellData->elemCount; } return 0; } -int Blockmap::iterate(Cell const &cell, int (*callback) (void *, void *), - void *context) const +LoopResult Blockmap::forAllInCell(Cell const &cell, std::function func) const { - if(!callback) return false; // Huh? - - if(CellData *cellData = d->cellData(cell)) + if(auto *cellData = d->cellData(cell)) { RingNode *node = cellData->ringNodes; while(node) { RingNode *next = node->next; - if(node->elem) { - if(int result = callback(node->elem, context)) - return result; // Stop iteration. + if(auto result = func(node->elem)) return result; } - node = next; } } - return false; // Continue iteration. + return LoopContinue; } -int Blockmap::iterate(AABoxd const ®ion, int (*callback) (void *, void *), - void *context) const +LoopResult Blockmap::forAllInBox(AABoxd const &box, std::function func) const { - if(!callback) return false; // Huh? - - CellBlock cellBlock = toCellBlock(region); + CellBlock cellBlock = toCellBlock(box); d->clipBlock(cellBlock); Cell cell; for(cell.y = cellBlock.min.y; cell.y < cellBlock.max.y; ++cell.y) for(cell.x = cellBlock.min.x; cell.x < cellBlock.max.x; ++cell.x) { - if(int result = iterate(cell, callback, context)) - return result; + if(auto result = forAllInCell(cell, func)) return result; } - - return false; // Continue iteration. + return LoopContinue; } -int Blockmap::iterate(Vector2d const &from_, Vector2d const &to_, - int (*callback) (void *, void *), void *context) const +LoopResult Blockmap::forAllInPath(Vector2d const &from_, Vector2d const &to_, + std::function func) const { // We may need to clip and/or adjust these points. Vector2d from = from_; @@ -587,7 +574,7 @@ int Blockmap::iterate(Vector2d const &from_, Vector2d const &to_, if(!(from.x >= d->bounds.minX && from.x <= d->bounds.maxX && from.y >= d->bounds.minY && from.y <= d->bounds.maxY)) { - return false; + return LoopContinue; } // Check the easy case of a trace line completely outside the blockmap. @@ -596,7 +583,7 @@ int Blockmap::iterate(Vector2d const &from_, Vector2d const &to_, (from.y < d->bounds.minY && to.y < d->bounds.minY) || (from.y > d->bounds.maxY && to.y > d->bounds.maxY)) { - return false; + return LoopContinue; } /* @@ -707,8 +694,8 @@ int Blockmap::iterate(Vector2d const &from_, Vector2d const &to_, for(int pass = 0; pass < 64; ++pass) // Prevent a round off error leading us into // an infinite loop... { - if(int result = iterate(cell, callback, context)) - return result; // Early out. + if(auto result = forAllInCell(cell, func)) + return result; if(cell == destCell) break; @@ -724,7 +711,7 @@ int Blockmap::iterate(Vector2d const &from_, Vector2d const &to_, } } - return false; // Continue iteration. + return LoopContinue; } // Debug visual ---------------------------------------------------------------- diff --git a/doomsday/client/src/world/contactspreader.cpp b/doomsday/client/src/world/contactspreader.cpp index 56247cea67..5765968bc7 100644 --- a/doomsday/client/src/world/contactspreader.cpp +++ b/doomsday/client/src/world/contactspreader.cpp @@ -105,7 +105,11 @@ struct ContactSpreader _spreadBlocks->setBit(cellIndex); } - _blockmap.iterate(cell, spreadContactWorker, this); + _blockmap.forAllInCell(cell, [this] (void *element) + { + spreadContact(*static_cast(element)); + return LoopContinue; + }); } } @@ -258,13 +262,6 @@ struct ContactSpreader } while((hedge = &hedge->next()) != base); } - - static int spreadContactWorker(void *contact, void *context) - { - ContactSpreader *inst = static_cast(context); - inst->spreadContact(*static_cast(contact)); - return false; // Continue iteration. - } }; } // internal diff --git a/doomsday/client/src/world/generator.cpp b/doomsday/client/src/world/generator.cpp index 86f8fb9fd5..44f4788271 100644 --- a/doomsday/client/src/world/generator.cpp +++ b/doomsday/client/src/world/generator.cpp @@ -580,73 +580,6 @@ static int manyNewParticlesWorker(thinker_t *th, void *context) return false; // Continue iteration. } -struct checklineworker_params_t -{ - AABoxd mbox; - fixed_t tmpz, tmprad, tmpx1, tmpx2, tmpy1, tmpy2; - bool tmcross; - Line *ptcHitLine; -}; - -static int checkLineWorker(Line *ld, void *context) -{ - checklineworker_params_t &parm = *static_cast(context); - - // Does the bounding box miss the line completely? - if(parm.mbox.maxX <= ld->aaBox().minX || parm.mbox.minX >= ld->aaBox().maxX || - parm.mbox.maxY <= ld->aaBox().minY || parm.mbox.minY >= ld->aaBox().maxY) - return false; - - // Movement must cross the line. - if((ld->pointOnSide(Vector2d(FIX2FLT(parm.tmpx1), FIX2FLT(parm.tmpy1))) < 0) == - (ld->pointOnSide(Vector2d(FIX2FLT(parm.tmpx2), FIX2FLT(parm.tmpy2))) < 0)) - return false; - - /* - * We are possibly hitting something here. - */ - - // Bounce if we hit a solid wall. - /// @todo fixme: What about "one-way" window lines? - parm.ptcHitLine = ld; - if(!ld->hasBackSector()) return true; // Boing! - - Sector *front = ld->frontSectorPtr(); - Sector *back = ld->backSectorPtr(); - - // Determine the opening we have here. - /// @todo Use R_OpenRange() - fixed_t ceil; - if(front->ceiling().height() < back->ceiling().height()) - { - ceil = FLT2FIX(front->ceiling().height()); - } - else - { - ceil = FLT2FIX(back->ceiling().height()); - } - - fixed_t floor; - if(front->floor().height() > back->floor().height()) - { - floor = FLT2FIX(front->floor().height()); - } - else - { - floor = FLT2FIX(back->floor().height()); - } - - // There is a backsector. We possibly might hit something. - if(parm.tmpz - parm.tmprad < floor || parm.tmpz + parm.tmprad > ceil) - return true; // Boing! - - // There is a possibility that the new position is in a new sector. - parm.tmcross = true; // Afterwards, update the sector pointer. - - // False alarm, continue checking. - return false; -} - /** * Particle touches something solid. Returns false iff the particle dies. */ @@ -897,6 +830,13 @@ void Generator::moveParticle(int index) fixed_t x = pinfo->origin[VX] + pinfo->mov[VX]; fixed_t y = pinfo->origin[VY] + pinfo->mov[VY]; + struct checklineworker_params_t + { + AABoxd box; + fixed_t tmpz, tmprad, tmpx1, tmpx2, tmpy1, tmpy2; + bool tmcross; + Line *ptcHitLine; + }; checklineworker_params_t clParm; zap(clParm); clParm.tmcross = false; // Has crossed potential sector boundary? @@ -964,15 +904,82 @@ void Generator::moveParticle(int index) vec2d_t point; V2d_Set(point, FIX2FLT(MIN_OF(x, pinfo->origin[VX]) - st->radius), FIX2FLT(MIN_OF(y, pinfo->origin[VY]) - st->radius)); - V2d_InitBox(clParm.mbox.arvec2, point); + V2d_InitBox(clParm.box.arvec2, point); V2d_Set(point, FIX2FLT(MAX_OF(x, pinfo->origin[VX]) + st->radius), FIX2FLT(MAX_OF(y, pinfo->origin[VY]) + st->radius)); - V2d_AddToBox(clParm.mbox.arvec2, point); + V2d_AddToBox(clParm.box.arvec2, point); // Iterate the lines in the contacted blocks. validCount++; - if(map().lineBoxIterator(clParm.mbox, checkLineWorker, &clParm)) + DENG2_ASSERT(!clParm.ptcHitLine); + map().forAllLinesInBox(clParm.box, [&clParm] (Line &line) + { + // Does the bounding box miss the line completely? + if(clParm.box.maxX <= line.aaBox().minX || clParm.box.minX >= line.aaBox().maxX || + clParm.box.maxY <= line.aaBox().minY || clParm.box.minY >= line.aaBox().maxY) + { + return LoopContinue; + } + + // Movement must cross the line. + if((line.pointOnSide(Vector2d(FIX2FLT(clParm.tmpx1), FIX2FLT(clParm.tmpy1))) < 0) == + (line.pointOnSide(Vector2d(FIX2FLT(clParm.tmpx2), FIX2FLT(clParm.tmpy2))) < 0)) + { + return LoopContinue; + } + + /* + * We are possibly hitting something here. + */ + + // Bounce if we hit a solid wall. + /// @todo fixme: What about "one-way" window lines? + clParm.ptcHitLine = &line; + if(!line.hasBackSector()) + { + return LoopAbort; // Boing! + } + + Sector *front = line.frontSectorPtr(); + Sector *back = line.backSectorPtr(); + + // Determine the opening we have here. + /// @todo Use R_OpenRange() + fixed_t ceil; + if(front->ceiling().height() < back->ceiling().height()) + { + ceil = FLT2FIX(front->ceiling().height()); + } + else + { + ceil = FLT2FIX(back->ceiling().height()); + } + + fixed_t floor; + if(front->floor().height() > back->floor().height()) + { + floor = FLT2FIX(front->floor().height()); + } + else + { + floor = FLT2FIX(back->floor().height()); + } + + // There is a backsector. We possibly might hit something. + if(clParm.tmpz - clParm.tmprad < floor || clParm.tmpz + clParm.tmprad > ceil) + { + return LoopAbort; // Boing! + } + + // False alarm, continue checking. + clParm.ptcHitLine = nullptr; + // There is a possibility that the new position is in a new sector. + clParm.tmcross = true; // Afterwards, update the sector pointer. + return LoopContinue; + }); + + if(clParm.ptcHitLine) { fixed_t normal[2], dotp; diff --git a/doomsday/client/src/world/interceptor.cpp b/doomsday/client/src/world/interceptor.cpp index aff5c51a6e..503349594b 100644 --- a/doomsday/client/src/world/interceptor.cpp +++ b/doomsday/client/src/world/interceptor.cpp @@ -1,7 +1,7 @@ /** @file interceptor.cpp World map element/object ray trace interceptor. * * @authors Copyright © 1999-2013 Jaakko Keränen - * @authors Copyright © 2005-2013 Daniel Swanson + * @authors Copyright © 2005-2014 Daniel Swanson * @authors Copyright © 1993-1996 by id Software, Inc. * * @par License @@ -19,14 +19,13 @@ * 02110-1301 USA */ -#include "de_base.h" #include "world/interceptor.h" -#include "world/worldsystem.h" // validCount -#include "world/p_object.h" - #include #include +#include "world/blockmap.h" +#include "world/p_object.h" +#include "world/worldsystem.h" // validCount using namespace de; @@ -41,7 +40,7 @@ struct ListNode template ObjectType &objectAs() const { - DENG2_ASSERT(object != 0); + DENG2_ASSERT(object); return *static_cast(object); } }; @@ -64,7 +63,7 @@ DENG2_PIMPL_NOREF(Interceptor) Vector2d to; int flags; ///< @ref pathTraverseFlags - Map *map; + Map *map = nullptr; LineOpening opening; // Array representation for ray geometry (used with legacy code). @@ -74,11 +73,10 @@ DENG2_PIMPL_NOREF(Interceptor) Instance(traverser_t callback, Vector2d const &from, Vector2d const &to, int flags, void *context) : callback(callback) - , context(context) - , from(from) - , to(to) - , flags(flags) - , map(0) + , context (context) + , from (from) + , to (to) + , flags (flags) { V2d_Set(fromV1, from.x, from.y); V2d_Set(directionV1, to.x - from.x, to.y - from.y); @@ -102,11 +100,12 @@ DENG2_PIMPL_NOREF(Interceptor) // Configure the static head and tail. head.distance = 0.0f; - head.next = &tail; - head.prev = 0; + head.next = &tail; + head.prev = nullptr; + tail.distance = 1.0f; - tail.prev = &head; - tail.next = 0; + tail.prev = &head; + tail.next = nullptr; } // Start reusing intercepts (may point to a sentinel but that is Ok). @@ -124,7 +123,8 @@ DENG2_PIMPL_NOREF(Interceptor) // Reset the trace. head.next = &tail; tail.prev = &head; - mru = 0; + + mru = nullptr; #undef MININTERCEPTS } @@ -139,7 +139,7 @@ DENG2_PIMPL_NOREF(Interceptor) */ void addIntercept(intercepttype_t type, float distance, void *object) { - DENG2_ASSERT(object != 0); + DENG2_ASSERT(object); // First reject vs our sentinels if(distance < head.distance) return; @@ -272,25 +272,28 @@ DENG2_PIMPL_NOREF(Interceptor) return false; // Continue iteration. } - static int interceptPathMobjWorker(mobj_t *mobj, void *context) - { - static_cast(context)->intercept(*mobj); - return false; // Continue iteration. - } - void runTrace() { /// @todo Store the intercept list internally? clearIntercepts(); - validCount++; + int const localValidCount = ++validCount; if(flags & PTF_LINE) { - map->linePathIterator(from, to, interceptPathLineWorker, this); + map->forAllLinesInPath(from, to, interceptPathLineWorker, this); } if(flags & PTF_MOBJ) { - map->mobjPathIterator(from, to, interceptPathMobjWorker, this); + map->mobjBlockmap().forAllInPath(from, to, [this, &localValidCount] (void *object) + { + mobj_t &mob = *(mobj_t *)object; + if(mob.validCount != localValidCount) // not yet processed + { + mob.validCount = localValidCount; + intercept(mob); + } + return LoopContinue; + }); } } }; diff --git a/doomsday/client/src/world/line.cpp b/doomsday/client/src/world/line.cpp index 4d0bbf519a..25d585bb4a 100644 --- a/doomsday/client/src/world/line.cpp +++ b/doomsday/client/src/world/line.cpp @@ -18,9 +18,12 @@ * 02110-1301 USA */ -#include "de_base.h" #include "world/line.h" +#include +#include +#include + #include "dd_main.h" // App_Materials(), verbose #include "m_misc.h" @@ -37,8 +40,6 @@ #ifdef __CLIENT__ # include "world/map.h" #endif -#include -#include #ifdef WIN32 # undef max @@ -49,37 +50,25 @@ using namespace de; DENG2_PIMPL_NOREF(Line::Side::Segment) { - /// Half-edge attributed to the line segment (not owned). - HEdge *hedge; + HEdge *hedge = nullptr; ///< Half-edge attributed to the line segment (not owned). #ifdef __CLIENT__ - /// Distance along the attributed map line at which the half-edge vertex occurs. - coord_t lineSideOffset; - - /// Accurate length of the segment. - coord_t length; - - bool frontFacing; + coord_t length = 0; ///< Accurate length of the segment. + coord_t lineSideOffset = 0; ///< Distance along the attributed map line at which the half-edge vertex occurs. + bool frontFacing = false; #endif - - Instance(HEdge *hedge) - : hedge(hedge) -#ifdef __CLIENT__ - , lineSideOffset(0) - , length(0) - , frontFacing(false) -#endif - {} }; Line::Side::Segment::Segment(Line::Side &lineSide, HEdge &hedge) : MapElement(DMU_SEGMENT, &lineSide) - , d(new Instance(&hedge)) -{} + , d(new Instance) +{ + d->hedge = &hedge; +} HEdge &Line::Side::Segment::hedge() const { - DENG2_ASSERT(d->hedge != 0); + DENG2_ASSERT(d->hedge); return *d->hedge; } @@ -117,30 +106,31 @@ void Line::Side::Segment::setFrontFacing(bool yes) #endif // __CLIENT__ -/** - * Line side section of which there are three (middle, bottom and top). - */ -struct Section -{ - Surface surface; - ThinkerT soundEmitter; - - Section(Line::Side &side) : surface(side) - { - //zap(soundEmitter); - } -}; - DENG2_PIMPL_NOREF(Line::Side) #ifdef __CLIENT__ , DENG2_OBSERVES(Line, FlagsChange) #endif { - int flags; ///< @ref sdefFlags - Sector *sector; ///< Attributed sector (not owned). - Segments segments; ///< On "this" side, sorted. - bool needSortSegments; ///< set to @c true when the list needs sorting. - int shadowVisCount; ///< Framecount of last time shadows were drawn. + int flags = 0; ///< @ref sdefFlags + Sector *sector = nullptr; ///< Attributed sector (not owned). + + typedef QList Segments; + Segments segments; ///< On "this" side, sorted. + bool needSortSegments = false; ///< set to @c true when the list needs sorting. + + int shadowVisCount = 0; ///< Framecount of last time shadows were drawn. + + /** + * Line side section of which there are three (middle, bottom and top). + */ + struct Section + { + Surface surface; + ThinkerT soundEmitter; + + Section(Line::Side &side) : surface(side) + {} + }; struct Sections { @@ -151,26 +141,16 @@ DENG2_PIMPL_NOREF(Line::Side) Sections(Side &side) : middle(side), bottom(side), top(side) {} }; - QScopedPointer sections; + std::unique_ptr sections; - Instance(Sector *sector) - : flags(0) - , sector(sector) - , needSortSegments(false) - , shadowVisCount(0) - {} - - ~Instance() - { - qDeleteAll(segments); - } + ~Instance() { qDeleteAll(segments); } /** * Retrieve the Section associated with @a sectionId. */ Section §ionById(int sectionId) { - if(!sections.isNull()) + if(sections) { switch(sectionId) { @@ -192,7 +172,7 @@ DENG2_PIMPL_NOREF(Line::Side) // We'll use a QMap for sorting the segments. QMap sortedSegs; - foreach(Segment *seg, segments) + for(Segment *seg : segments) { sortedSegs.insert((seg->hedge().origin() - lineSideOrigin).length(), seg); } @@ -203,15 +183,15 @@ DENG2_PIMPL_NOREF(Line::Side) /// Observes Line FlagsChange void lineFlagsChanged(Line &line, int oldFlags) { - if(!sections.isNull()) + if(sections) { if((line.flags() & DDLF_DONTPEGTOP) != (oldFlags & DDLF_DONTPEGTOP)) { - sections->top.surface.markAsNeedingDecorationUpdate(); + sections->top.surface.markForDecorationUpdate(); } if((line.flags() & DDLF_DONTPEGBOTTOM) != (oldFlags & DDLF_DONTPEGBOTTOM)) { - sections->bottom.surface.markAsNeedingDecorationUpdate(); + sections->bottom.surface.markForDecorationUpdate(); } } } @@ -220,8 +200,9 @@ DENG2_PIMPL_NOREF(Line::Side) Line::Side::Side(Line &line, Sector *sector) : MapElement(DMU_SIDE, &line) - , d(new Instance(sector)) + , d(new Instance) { + d->sector = sector; #ifdef __CLIENT__ line.audienceForFlagsChange += d; #endif @@ -259,7 +240,7 @@ bool Line::Side::considerOneSided() const bool Line::Side::hasSector() const { - return d->sector != 0; + return d->sector != nullptr; } Sector &Line::Side::sector() const @@ -274,7 +255,7 @@ Sector &Line::Side::sector() const bool Line::Side::hasSections() const { - return !d->sections.isNull(); + return bool(d->sections); } void Line::Side::addSections() @@ -292,7 +273,7 @@ Surface &Line::Side::surface(int sectionId) Surface const &Line::Side::surface(int sectionId) const { - return const_cast(const_cast(this)->surface(sectionId)); + return const_cast(this)->surface(sectionId); } SoundEmitter &Line::Side::soundEmitter(int sectionId) @@ -302,7 +283,7 @@ SoundEmitter &Line::Side::soundEmitter(int sectionId) SoundEmitter const &Line::Side::soundEmitter(int sectionId) const { - return const_cast(const_cast(this)->soundEmitter(sectionId)); + return const_cast(this)->soundEmitter(sectionId); } void Line::Side::clearSegments() @@ -314,7 +295,7 @@ void Line::Side::clearSegments() Line::Side::Segment *Line::Side::addSegment(de::HEdge &hedge) { // Have we an exiting segment for this half-edge? - foreach(Segment *seg, d->segments) + for(Segment *seg : d->segments) { if(&seg->hedge() == &hedge) return seg; @@ -331,18 +312,9 @@ Line::Side::Segment *Line::Side::addSegment(de::HEdge &hedge) return newSeg; } -Line::Side::Segments const &Line::Side::segments() const -{ - if(d->needSortSegments) - { - d->sortSegments(from().origin()); - } - return d->segments; -} - HEdge *Line::Side::leftHEdge() const { - if(d->segments.isEmpty()) return 0; + if(d->segments.isEmpty()) return nullptr; if(d->needSortSegments) { d->sortSegments(from().origin()); @@ -352,7 +324,7 @@ HEdge *Line::Side::leftHEdge() const HEdge *Line::Side::rightHEdge() const { - if(d->segments.isEmpty()) return 0; + if(d->segments.isEmpty()) return nullptr; if(d->needSortSegments) { d->sortSegments(from().origin()); @@ -372,7 +344,7 @@ void Line::Side::updateSoundEmitterOrigin(int sectionId) emitter.origin[VX] = lineCenter.x; emitter.origin[VY] = lineCenter.y; - DENG2_ASSERT(d->sector != 0); + DENG2_ASSERT(d->sector); coord_t const ffloor = d->sector->floor().height(); coord_t const fceil = d->sector->ceiling().height(); @@ -437,7 +409,7 @@ void Line::Side::updateSurfaceNormals() // All line side surfaces have the same normals. middle().setNormal(normal); // will normalize bottom().setNormal(normal); - top().setNormal(normal); + top ().setNormal(normal); } int Line::Side::flags() const @@ -451,7 +423,7 @@ void Line::Side::setFlags(int flagsToChange, FlagOp operation) } void Line::Side::chooseSurfaceTintColors(int sectionId, Vector3f const **topColor, - Vector3f const **bottomColor) const + Vector3f const **bottomColor) const { if(hasSections()) { @@ -460,7 +432,7 @@ void Line::Side::chooseSurfaceTintColors(int sectionId, Vector3f const **topColo case Middle: if(isFlagged(SDF_BLENDMIDTOTOP)) { - *topColor = &top().tintColor(); + *topColor = &top ().tintColor(); *bottomColor = &middle().tintColor(); } else if(isFlagged(SDF_BLENDMIDTOBOTTOM)) @@ -478,12 +450,12 @@ void Line::Side::chooseSurfaceTintColors(int sectionId, Vector3f const **topColo case Top: if(isFlagged(SDF_BLENDTOPTOMID)) { - *topColor = &top().tintColor(); + *topColor = &top ().tintColor(); *bottomColor = &middle().tintColor(); } else { - *topColor = &top().tintColor(); + *topColor = &top ().tintColor(); *bottomColor = 0; } return; @@ -529,7 +501,7 @@ void Line::Side::setShadowVisCount(int newCount) */ static Material *chooseFixMaterial(LineSide &side, int section) { - Material *choice1 = 0, *choice2 = 0; + Material *choice1 = nullptr, *choice2 = nullptr; Sector *frontSec = side.sectorPtr(); Sector *backSec = side.back().sectorPtr(); @@ -566,9 +538,11 @@ static Material *chooseFixMaterial(LineSide &side, int section) Line *other = R_FindLineNeighbor(frontSec, &side.line(), side.line().vertexOwner(side.sideId()), false /*next clockwise*/); if(!other) + { // Try the right neighbor. other = R_FindLineNeighbor(frontSec, &side.line(), side.line().vertexOwner(side.sideId()^1), true /*next anti-clockwise*/); + } if(other) { @@ -581,7 +555,7 @@ static Material *chooseFixMaterial(LineSide &side, int section) { // Compare the relative heights to decide. LineSide &otherSide = other->side(&other->frontSector() == frontSec? Line::Front : Line::Back); - Sector &otherSec = other->side(&other->frontSector() == frontSec? Line::Back : Line::Front).sector(); + Sector &otherSec = other->side(&other->frontSector() == frontSec? Line::Back : Line::Front).sector(); if(otherSec.ceiling().height() <= frontSec->floor().height()) choice1 = otherSide.top().materialPtr(); @@ -759,13 +733,13 @@ int Line::Side::setProperty(DmuArgs const &args) DENG2_PIMPL(Line) { - int flags; ///< Public DDLF_* flags. - Vertex *from; ///< Start vertex (not owned). - Vertex *to; ///< End vertex (not owned). - Vector2d direction; ///< From start to end vertex. - binangle_t angle; ///< Calculated from the direction vector. - slopetype_t slopeType; ///< Logical line slope (i.e., world angle) classification. - coord_t length; ///< Accurate length. + int flags; ///< Public DDLF_* flags. + Vertex *from; ///< Start vertex (not owned). + Vertex *to; ///< End vertex (not owned). + Vector2d direction; ///< From start to end vertex. + binangle_t angle; ///< Calculated from the direction vector. + slopetype_t slopeType; ///< Logical line slope (i.e., world angle) classification. + coord_t length; ///< Accurate length. ///< Map space bounding box encompassing both vertexes. AABoxd aaBox; @@ -774,8 +748,8 @@ DENG2_PIMPL(Line) Side front; Side back; - Polyobj *polyobj; ///< Polyobj "this" line defines a section of, if any. - int validCount; ///< Used by legacy algorithms to prevent repeated processing. + Polyobj *polyobj = nullptr; ///< Polyobj "this" line defines a section of, if any. + int validCount = 0; ///< Used by legacy algorithms to prevent repeated processing. /// Whether the line has been mapped by each player yet. bool mapped[DDMAXPLAYERS]; @@ -783,19 +757,17 @@ DENG2_PIMPL(Line) Instance(Public *i, Vertex &from, Vertex &to, int flags, Sector *frontSector, Sector *backSector) : Base(i) - , flags(flags) - , from(&from) - , to(&to) + , flags (flags) + , from (&from) + , to (&to) , direction(to.origin() - from.origin()) - , angle(bamsAtan2(int( direction.y ), int( direction.x ))) + , angle (bamsAtan2(int( direction.y ), int( direction.x ))) , slopeType(M_SlopeTypeXY(direction.x, direction.y)) - , length(direction.length()) - , front(*i, frontSector) - , back(*i, backSector) - , polyobj(0) - , validCount(0) + , length (direction.length()) + , front (*i, frontSector) + , back (*i, backSector) { - zap(mapped); + de::zap(mapped); } void notifyFlagsChanged(int oldFlags) @@ -806,9 +778,6 @@ DENG2_PIMPL(Line) Line::Line(Vertex &from, Vertex &to, int flags, Sector *frontSector, Sector *backSector) : MapElement(DMU_LINE) - , _vo1(0) - , _vo2(0) - , _bspWindowSector(0) , d(new Instance(this, from, to, flags, frontSector, backSector)) { updateAABox(); @@ -837,12 +806,12 @@ void Line::setFlags(int flagsToChange, FlagOp operation) bool Line::isBspWindow() const { - return _bspWindowSector != 0; + return _bspWindowSector != nullptr; } bool Line::definesPolyobj() const { - return d->polyobj != 0; + return d->polyobj != nullptr; } Polyobj &Line::polyobj() const @@ -862,18 +831,18 @@ void Line::setPolyobj(Polyobj *newPolyobj) Line::Side &Line::side(int back) { - return back? d->back : d->front; + return (back? d->back : d->front); } Line::Side const &Line::side(int back) const { - return back? d->back : d->front; + return (back? d->back : d->front); } Vertex &Line::vertex(int to) const { - DENG2_ASSERT((to? d->to : d->from) != 0); - return to? *d->to : *d->from; + DENG2_ASSERT((to? d->to : d->from) != nullptr); + return (to? *d->to : *d->from); } void Line::replaceVertex(int to, Vertex &newVertex) @@ -1092,6 +1061,6 @@ int Line::setProperty(DmuArgs const &args) LineOwner *Line::vertexOwner(int to) const { - DENG2_ASSERT((to? _vo2 : _vo1) != 0); - return to? _vo2 : _vo1; + DENG2_ASSERT((to? _vo2 : _vo1) != nullptr); + return (to? _vo2 : _vo1); } diff --git a/doomsday/client/src/world/linesighttest.cpp b/doomsday/client/src/world/linesighttest.cpp index a94422f30f..2a4162a8e3 100644 --- a/doomsday/client/src/world/linesighttest.cpp +++ b/doomsday/client/src/world/linesighttest.cpp @@ -239,8 +239,8 @@ DENG2_PIMPL(LineSightTest) ConvexSubspace const &subspace = bspLeaf.subspace(); // Check polyobj lines. - foreach(Polyobj *po, subspace.polyobjs()) - foreach(Line *line, po->lines()) + for(Polyobj *po : subspace.polyobjs()) + for(Line *line : po->lines()) { if(!crossLine(line->front())) return false; // Stop traversal. @@ -258,8 +258,8 @@ DENG2_PIMPL(LineSightTest) } } while((hedge = &hedge->next()) != base); - foreach(Mesh *mesh, subspace.extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) + for(Mesh *mesh : subspace.extraMeshes()) + for(HEdge *hedge : mesh->hedges()) { // Is this on the back of a one-sided line? if(!hedge->hasMapElement()) diff --git a/doomsday/client/src/world/map.cpp b/doomsday/client/src/world/map.cpp index f06810d9fd..365c9de78d 100644 --- a/doomsday/client/src/world/map.cpp +++ b/doomsday/client/src/world/map.cpp @@ -98,11 +98,21 @@ static int lgMXSample = 1; ///< 5 samples per block. Cvar. namespace de { +static inline WorldSystem &worldSys() +{ + return App_WorldSystem(); +} + struct EditableElements { - Map::Lines lines; - Map::Sectors sectors; - Map::Polyobjs polyobjs; + typedef QList Lines; + Lines lines; + + typedef QList Sectors; + Sectors sectors; + + typedef QList Polyobjs; + Polyobjs polyobjs; EditableElements() {} @@ -121,7 +131,7 @@ DENG2_PIMPL(Map) , DENG2_OBSERVES(ThinkerData, Deletion) #endif { - bool editingEnabled; + bool editingEnabled = true; EditableElements editable; MapDef *def; ///< Definition for the map (not owned, may be @c NULL). @@ -129,15 +139,19 @@ DENG2_PIMPL(Map) Mesh mesh; ///< All map geometries. + typedef QList Sectors; Sectors sectors; + + typedef QList Lines; Lines lines; + + typedef QList Polyobjs; Polyobjs polyobjs; struct Bsp { BspTree *tree; ///< Owns the BspElements. - Bsp() : tree(0) {} ~Bsp() { clear(); } @@ -156,6 +170,7 @@ DENG2_PIMPL(Map) } } bsp; + typedef QList Subspaces; Subspaces subspaces; typedef QMultiMap SectorClusters; @@ -219,7 +234,7 @@ DENG2_PIMPL(Map) nodepile_t mobjNodes; nodepile_t lineNodes; - nodeindex_t *lineLinks; ///< Indices to roots. + nodeindex_t *lineLinks = nullptr; ///< Indices to roots. #ifdef __CLIENT__ PlaneSet trackedPlanes; @@ -298,32 +313,25 @@ DENG2_PIMPL(Map) /// Shadow Bias data. struct Bias { - uint currentTime; ///< The "current" frame in milliseconds. - uint lastChangeOnFrame; - BiasSources sources; ///< All bias light sources (owned). + uint currentTime = 0; ///< The "current" frame in milliseconds. + uint lastChangeOnFrame = 0; - Bias() : currentTime(0), lastChangeOnFrame(0) - {} + typedef QList BiasSources; + BiasSources sources; ///< All bias light sources (owned). } bias; + typedef QList Lumobjs; Lumobjs lumobjs; ///< All lumobjs (owned). QScopedPointer decorator; - coord_t skyFloorHeight; - coord_t skyCeilingHeight; + coord_t skyFloorHeight = DDMAXFLOAT; + coord_t skyCeilingHeight = DDMINFLOAT; ClMobjHash clMobjHash; #endif - Instance(Public *i) - : Base (i) - , editingEnabled (true) - , lineLinks (0) -#ifdef __CLIENT__ - , skyFloorHeight (DDMAXFLOAT) - , skyCeilingHeight(DDMINFLOAT) -#endif + Instance(Public *i) : Base(i) { sky.setMap(thisPublic); sky.setIndexInMap(0); @@ -346,7 +354,7 @@ DENG2_PIMPL(Map) qDeleteAll(subspaces); qDeleteAll(sectors); - foreach(Polyobj *polyobj, polyobjs) + for(Polyobj *polyobj : polyobjs) { polyobj->~Polyobj(); M_Free(polyobj); @@ -355,7 +363,7 @@ DENG2_PIMPL(Map) #ifdef __CLIENT__ // Stop observing client mobjs. - foreach(mobj_t *mo, clMobjHash) + for(mobj_t *mo : clMobjHash) { THINKER_DATA(mo->thinker, ThinkerData).audienceForDeletion() -= this; } @@ -365,18 +373,13 @@ DENG2_PIMPL(Map) // mobjNodes/lineNodes/lineLinks } - inline WorldSystem &worldSys() - { - return App_WorldSystem(); - } - /** * @pre Axis-aligned bounding boxes of all Sectors must be initialized. */ void updateBounds() { bool haveGeometry = false; - foreach(Line *line, lines) + for(Line *line : lines) { // Polyobj lines don't count. if(line->definesPolyobj()) continue; @@ -399,10 +402,7 @@ DENG2_PIMPL(Map) void unclosedSectorFound(Sector §or, Vector2d const &nearPoint) { // Notify interested parties that an unclosed sector was found. - DENG2_FOR_PUBLIC_AUDIENCE(UnclosedSectorFound, i) - { - i->unclosedSectorFound(sector, nearPoint); - } + DENG2_FOR_PUBLIC_AUDIENCE(UnclosedSectorFound, i) i->unclosedSectorFound(sector, nearPoint); } /** @@ -413,25 +413,20 @@ DENG2_PIMPL(Map) */ void notifyOneWayWindowFound(Line &line, Sector &backFacingSector) { - DENG2_FOR_PUBLIC_AUDIENCE(OneWayWindowFound, i) - { - i->oneWayWindowFound(line, backFacingSector); - } + DENG2_FOR_PUBLIC_AUDIENCE(OneWayWindowFound, i) i->oneWayWindowFound(line, backFacingSector); } struct testForWindowEffectParams { - double frontDist, backDist; - Sector *frontOpen, *backOpen; - Line *frontLine, *backLine; - Line *testLine; + double frontDist = 0; + double backDist = 0; + Sector *frontOpen = nullptr; + Sector *backOpen = nullptr; + Line *frontLine = nullptr; + Line *backLine = nullptr; + Line *testLine = nullptr; + bool castHorizontal = false; Vector2d testLineCenter; - bool castHorizontal; - - testForWindowEffectParams() - : frontDist(0), backDist(0), frontOpen(0), backOpen(0), - frontLine(0), backLine(0), testLine(0), castHorizontal(false) - {} }; static void testForWindowEffect2(Line &line, testForWindowEffectParams &p) @@ -510,12 +505,6 @@ DENG2_PIMPL(Map) } } - static int testForWindowEffectWorker(Line *line, void *parms) - { - testForWindowEffect2(*line, *reinterpret_cast(parms)); - return false; // Continue iteration. - } - bool lineMightHaveWindowEffect(Line const &line) { if(line.definesPolyobj()) return false; @@ -538,7 +527,7 @@ DENG2_PIMPL(Map) void findOneWayWindows() { - foreach(Vertex *vertex, mesh.vertexes()) + for(Vertex *vertex : mesh.vertexs()) { // Count the total number of one and two-sided line owners for each // vertex. (Used in the process of locating window effect lines.) @@ -546,7 +535,7 @@ DENG2_PIMPL(Map) } // Search for "one-way window" effects. - foreach(Line *line, lines) + for(Line *line : lines) { if(!lineMightHaveWindowEffect(*line)) continue; @@ -568,8 +557,13 @@ DENG2_PIMPL(Map) scanRegion.minX = line->aaBox().minX - bsp::DIST_EPSILON; scanRegion.maxX = line->aaBox().maxX + bsp::DIST_EPSILON; } + validCount++; - self.lineBoxIterator(scanRegion, LIF_SECTOR, testForWindowEffectWorker, &p); + self.forAllLinesInBox(scanRegion, LIF_SECTOR, [&p] (Line &line) + { + testForWindowEffect2(line, p); + return LoopContinue; + }); if(p.backOpen && p.frontOpen && line->frontSectorPtr() == p.backOpen) { @@ -632,7 +626,7 @@ DENG2_PIMPL(Map) // Attribute an index to any new vertexes. for(int i = nextVertexOrd; i < mesh.vertexCount(); ++i) { - Vertex *vtx = mesh.vertexes().at(i); + Vertex *vtx = mesh.vertexs().at(i); vtx->setMap(thisPublic); vtx->setIndexInMap(i); } @@ -903,6 +897,11 @@ DENG2_PIMPL(Map) { if(!mo || !line) return; + // Lines with only one sector will not be linked to because a mobj can't + // legally cross one. + if(!line->hasFrontSector()) return; + if(!line->hasBackSector()) return; + // Add a node to the mobj's ring. nodeindex_t nodeIndex = NP_New(&mobjNodes, line); NP_Link(&mobjNodes, nodeIndex, mo->lineRoot); @@ -913,58 +912,33 @@ DENG2_PIMPL(Map) NP_Link(&lineNodes, nodeIndex, lineLinks[line->indexInMap()]); } - struct LineLinkerParams - { - Map *map; - mobj_t *mo; - AABoxd moAABox; - }; - - /** - * The given line might cross the mobj. If necessary, link the mobj into - * the line's mobj link ring. - */ - static int lineLinkerWorker(Line *line, void *context) - { - LineLinkerParams &parm = *static_cast(context); - - // Do the bounding boxes intercept? - if(parm.moAABox.minX >= line->aaBox().maxX || - parm.moAABox.minY >= line->aaBox().maxY || - parm.moAABox.maxX <= line->aaBox().minX || - parm.moAABox.maxY <= line->aaBox().minY) - { - return false; - } - - // Line does not cross the mobj's bounding box? - if(line->boxOnSide(parm.moAABox)) return false; - - // Lines with only one sector will not be linked to because a mobj can't - // legally cross one. - if(!line->hasFrontSector()) return false; - if(!line->hasBackSector()) return false; - - parm.map->d->linkMobjToLine(parm.mo, line); - return false; - } - /** * @note Caller must ensure that the mobj is @em not linked. */ void linkMobjToLines(mobj_t &mo) { + AABoxd const box = Mobj_AABox(mo); + // Get a new root node. mo.lineRoot = NP_New(&mobjNodes, NP_ROOT_NODE); - // Set up a line iterator for doing the linking. - LineLinkerParams parm; zap(parm); - parm.map = &self; - parm.mo = &mo; - parm.moAABox = Mobj_AABox(mo); - validCount++; - self.lineBoxIterator(parm.moAABox, lineLinkerWorker, &parm); + self.forAllLinesInBox(box, [this, &mo, &box] (Line &line) + { + // Do the bounding boxes intercept? + if(!(box.minX >= line.aaBox().maxX || + box.minY >= line.aaBox().maxY || + box.maxX <= line.aaBox().minX || + box.maxY <= line.aaBox().minY)) + { + // Line crosses the mobj's bounding box? + if(!line.boxOnSide(box)) + { + this->linkMobjToLine(&mo, &line); + } + } + return LoopContinue; + }); } /** @@ -1259,7 +1233,7 @@ DENG2_PIMPL(Map) * Apply changes to all surfaces: */ bias.lastChangeOnFrame = R_FrameCount(); - foreach(SectorCluster *cluster, clusters) + for(SectorCluster *cluster : clusters) { cluster->applyBiasDigest(allChanges); } @@ -1270,26 +1244,27 @@ DENG2_PIMPL(Map) */ void generateMobjContacts() { - foreach(Sector *sector, sectors) + for(Sector *sector : sectors) for(mobj_t *iter = sector->firstMobj(); iter; iter = iter->sNext) { R_AddContact(*iter); } } - void generateLumobjs(QList const &decorList) const + void generateLumobjs(Surface const &surface) { - foreach(Decoration const *decor, decorList) + surface.forAllDecorations([this] (Decoration &decor) { - if(LightDecoration const *decorLight = decor->maybeAs()) + if(LightDecoration const *decorLight = decor.maybeAs()) { - QScopedPointerlum(decorLight->generateLumobj()); - if(!lum.isNull()) + std::unique_ptr lum(decorLight->generateLumobj()); + if(lum) { self.addLumobj(*lum); // a copy is made. } } - } + return LoopContinue; + }); } /** @@ -1585,10 +1560,6 @@ DENG2_PIMPL(Map) Map::Map(MapDef *mapDefinition) : d(new Instance(this)) { - _globalGravity = 0; - _effectiveGravity = 0; - _ambientLightLevel = 0; - setDef(mapDefinition); } @@ -1609,7 +1580,7 @@ Mesh const &Map::mesh() const bool Map::hasBspTree() const { - return d->bsp.tree != 0; + return d->bsp.tree != nullptr; } Map::BspTree const &Map::bspTree() const @@ -1622,34 +1593,6 @@ Map::BspTree const &Map::bspTree() const throw MissingBspTreeError("Map::bspTree", "No BSP tree is available"); } -Map::Subspaces const &Map::subspaces() const -{ - return d->subspaces; -} - -LoopResult Map::forAllClusters(Sector *sector, std::function func) -{ - if(sector) - { - for(auto it = d->clusters.constFind(sector); it != d->clusters.end() && it.key() == sector; ++it) - { - if(auto result = func(**it)) return result; - } - return LoopContinue; - } - - for(SectorCluster *cluster : d->clusters) - { - if(auto result = func(*cluster)) return result; - } - return LoopContinue; -} - -int Map::clusterCount() const -{ - return d->clusters.count(); -} - #ifdef __CLIENT__ bool Map::hasLightGrid() @@ -2123,24 +2066,193 @@ Sky &Map::sky() const return d->sky; } -Map::Vertexes const &Map::vertexes() const +int Map::vertexCount() const +{ + return d->mesh.vertexCount(); +} + +Vertex &Map::vertex(int index) const +{ + if(Vertex *vtx = vertexPtr(index)) return *vtx; + /// @throw MissingElementError Invalid Vertex reference specified. + throw MissingElementError("Map::vertex", "Unknown Vertex index:" + String::number(index)); +} + +Vertex *Map::vertexPtr(int index) const +{ + if(index >= 0 && index < d->mesh.vertexCount()) + { + return d->mesh.vertexs().at(index); + } + return nullptr; +} + +LoopResult Map::forAllVertexs(std::function func) const +{ + for(Vertex *vtx : d->mesh.vertexs()) + { + if(auto result = func(*vtx)) return result; + } + return LoopContinue; +} + +int Map::lineCount() const +{ + return d->lines.count(); +} + +Line &Map::line(int index) const +{ + if(Line *li = linePtr(index)) return *li; + /// @throw MissingElementError Invalid Line reference specified. + throw MissingElementError("Map::line", "Unknown Line index:" + String::number(index)); +} + +Line *Map::linePtr(int index) const +{ + if(index >= 0 && index < d->lines.count()) + { + return d->lines.at(index); + } + return nullptr; +} + +LoopResult Map::forAllLines(std::function func) const { - return d->mesh.vertexes(); + for(Line *li : d->lines) + { + if(auto result = func(*li)) return result; + } + return LoopContinue; } -Map::Lines const &Map::lines() const +int Map::sectorCount() const { - return d->lines; + return d->sectors.count(); } -Map::Sectors const &Map::sectors() const +Sector &Map::sector(int index) const { - return d->sectors; + if(Sector *sec = sectorPtr(index)) return *sec; + /// @throw MissingElementError Invalid Sector reference specified. + throw MissingElementError("Map::sector", "Unknown Sector index:" + String::number(index)); } -Map::Polyobjs const &Map::polyobjs() const +Sector *Map::sectorPtr(int index) const { - return d->polyobjs; + if(index >= 0 && index < d->sectors.count()) + { + return d->sectors.at(index); + } + return nullptr; +} + +LoopResult Map::forAllSectors(std::function func) const +{ + for(Sector *sec : d->sectors) + { + if(auto result = func(*sec)) return result; + } + return LoopContinue; +} + +int Map::subspaceCount() const +{ + return d->subspaces.count(); +} + +ConvexSubspace &Map::subspace(int index) const +{ + if(ConvexSubspace *sub = subspacePtr(index)) return *sub; + /// @throw MissingElementError Invalid ConvexSubspace reference specified. + throw MissingElementError("Map::subspace", "Unknown subspace index:" + String::number(index)); +} + +ConvexSubspace *Map::subspacePtr(int index) const +{ + if(index >= 0 && index < d->subspaces.count()) + { + return d->subspaces.at(index); + } + return nullptr; +} + +LoopResult Map::forAllSubspaces(std::function func) const +{ + for(ConvexSubspace *sub : d->subspaces) + { + if(auto result = func(*sub)) return result; + } + return LoopContinue; +} + +int Map::clusterCount() const +{ + return d->clusters.count(); +} + +LoopResult Map::forAllClusters(Sector *sector, std::function func) +{ + if(sector) + { + for(auto it = d->clusters.constFind(sector); it != d->clusters.end() && it.key() == sector; ++it) + { + if(auto result = func(**it)) return result; + } + return LoopContinue; + } + + for(SectorCluster *cluster : d->clusters) + { + if(auto result = func(*cluster)) return result; + } + return LoopContinue; +} + +int Map::polyobjCount() const +{ + return d->polyobjs.count(); +} + +Polyobj &Map::polyobj(int index) const +{ + if(Polyobj *pob = polyobjPtr(index)) return *pob; + /// @throw MissingObjectError Invalid ConvexSubspace reference specified. + throw MissingObjectError("Map::subspace", "Unknown Polyobj index:" + String::number(index)); +} + +Polyobj *Map::polyobjPtr(int index) const +{ + if(index >= 0 && index < d->polyobjs.count()) + { + return d->polyobjs.at(index); + } + return nullptr; +} + +LoopResult Map::forAllPolyobjs(std::function func) const +{ + for(Polyobj *pob : d->polyobjs) + { + if(auto result = func(*pob)) return result; + } + return LoopContinue; +} + +void Map::initPolyobjs() +{ + LOG_AS("Map::initPolyobjs"); + + for(Polyobj *po : d->polyobjs) + { + /// @todo Is this still necessary? -ds + /// (This data is updated automatically when moving/rotating). + po->updateAABox(); + po->updateSurfaceTangents(); + + po->unlink(); + po->link(); + } } int Map::ambientLightLevel() const @@ -2148,18 +2260,25 @@ int Map::ambientLightLevel() const return _ambientLightLevel; } -int Map::toSideIndex(int lineIndex, int backSide) // static +LineSide &Map::side(int index) const { - DENG_ASSERT(lineIndex >= 0); - return lineIndex * 2 + (backSide? 1 : 0); + if(LineSide *side = sidePtr(index)) return *side; + /// @throw MissingElementError Invalid LineSide reference specified. + throw MissingElementError("Map::side", "Unknown LineSide index:" + String::number(index)); } -LineSide *Map::sideByIndex(int index) const +LineSide *Map::sidePtr(int index) const { - if(index < 0) return 0; + if(index < 0) return nullptr; return &d->lines.at(index / 2)->side(index % 2); } +int Map::toSideIndex(int lineIndex, int backSide) // static +{ + DENG_ASSERT(lineIndex >= 0); + return lineIndex * 2 + (backSide? 1 : 0); +} + bool Map::identifySoundEmitter(SoundEmitter const &emitter, Sector **sector, Polyobj **poly, Plane **plane, Surface **surface) const { @@ -2191,32 +2310,6 @@ bool Map::identifySoundEmitter(SoundEmitter const &emitter, Sector **sector, return (*sector != 0 || *poly != 0|| *plane != 0|| *surface != 0); } -Polyobj *Map::polyobjByTag(int tag) const -{ - foreach(Polyobj *polyobj, d->polyobjs) - { - if(polyobj->tag == tag) - return polyobj; - } - return 0; -} - -void Map::initPolyobjs() -{ - LOG_AS("Map::initPolyobjs"); - - foreach(Polyobj *po, d->polyobjs) - { - /// @todo Is this still necessary? -ds - /// (This data is updated automatically when moving/rotating). - po->updateAABox(); - po->updateSurfaceTangents(); - - po->unlink(); - po->link(); - } -} - EntityDatabase &Map::entityDatabase() const { return d->entityDatabase; @@ -2285,150 +2378,8 @@ Blockmap const &Map::subspaceBlockmap() const throw MissingBlockmapError("Map::subspaceBlockmap", "Convex subspace blockmap is not initialized"); } -struct blockmapcellmobjsiterator_params_t -{ - int localValidCount; - int (*callback) (mobj_t *, void *); - void *context; -}; - -static int blockmapCellMobjsIterator(void *object, void *context) -{ - mobj_t *mobj = static_cast(object); - blockmapcellmobjsiterator_params_t &parm = *static_cast(context); - - if(mobj->validCount != parm.localValidCount) - { - // This mobj has now been processed for the current iteration. - mobj->validCount = parm.localValidCount; - - // Action the callback. - if(int result = parm.callback(mobj, parm.context)) - return result; // Stop iteration. - } - - return false; // Continue iteration. -} - -int Map::mobjBoxIterator(AABoxd const &box, int (*callback) (mobj_t *, void *), - void *context) const -{ - if(!d->mobjBlockmap.isNull()) - { - blockmapcellmobjsiterator_params_t parm; zap(parm); - parm.localValidCount = validCount; - parm.callback = callback; - parm.context = context; - - return d->mobjBlockmap->iterate(box, blockmapCellMobjsIterator, &parm); - } - /// @throw MissingBlockmapError The mobj blockmap is not yet initialized. - throw MissingBlockmapError("Map::mobjBoxIterator", "Mobj blockmap is not initialized"); -} - -int Map::mobjPathIterator(Vector2d const &from, Vector2d const &to, - int (*callback)(mobj_t *, void *), void *context) const -{ - if(!d->mobjBlockmap.isNull()) - { - blockmapcellmobjsiterator_params_t parm; zap(parm); - parm.localValidCount = validCount; - parm.callback = callback; - parm.context = context; - - return d->mobjBlockmap->iterate(from, to, blockmapCellMobjsIterator, &parm); - } - /// @throw MissingBlockmapError The mobj blockmap is not yet initialized. - throw MissingBlockmapError("Map::mobjPathIterator", "Mobj blockmap is not initialized"); -} - -struct blockmapcelllinesiterator_params_t -{ - int localValidCount; - int (*callback) (Line *, void *); - void *context; -}; - -static int blockmapCellLinesIterator(void *mapElement, void *context) -{ - Line *line = static_cast(mapElement); - blockmapcelllinesiterator_params_t &parm = *static_cast(context); - - if(line->validCount() != parm.localValidCount) - { - // This line has now been processed for the current iteration. - line->setValidCount(parm.localValidCount); - - // Action the callback. - if(int result = parm.callback(line, parm.context)) - return result; // Stop iteration. - } - - return false; // Continue iteration. -} - -struct blockmapcellsubspacesiterator_params_t -{ - AABoxd const *box; - int localValidCount; - int (*callback) (ConvexSubspace *, void *); - void *context; -}; - -static int blockmapCellSubspacesIterator(void *object, void *context) -{ - ConvexSubspace *subspace = static_cast(object); - blockmapcellsubspacesiterator_params_t &parm = *static_cast(context); - - if(subspace->validCount() != parm.localValidCount) - { - // This subspace has now been processed for the current iteration. - subspace->setValidCount(parm.localValidCount); - - // Check the bounds. - AABoxd const &leafAABox = subspace->poly().aaBox(); - if(parm.box) - { - if(leafAABox.maxX < parm.box->minX || - leafAABox.minX > parm.box->maxX || - leafAABox.minY > parm.box->maxY || - leafAABox.maxY < parm.box->minY) - { - return false; // Continue iteration. - } - } - - // Action the callback. - if(int result = parm.callback(subspace, parm.context)) - return result; // Stop iteration. - } - - return false; // Continue iteration. -} - -int Map::subspaceBoxIterator(AABoxd const &box, int (*callback) (ConvexSubspace *, void *), +int Map::forAllLinesTouchingMobj(mobj_t *mo, int (*callback) (Line *, void *), void *context) const -{ - if(!d->subspaceBlockmap.isNull()) - { - static int localValidCount = 0; - // This is only used here. - localValidCount++; - - blockmapcellsubspacesiterator_params_t parm; zap(parm); - parm.localValidCount = localValidCount; - parm.callback = callback; - parm.context = context; - parm.box = &box; - - return d->subspaceBlockmap->iterate(box, blockmapCellSubspacesIterator, &parm); - } - /// @throw MissingBlockmapError The subspace blockmap is not yet initialized. - throw MissingBlockmapError("Map::subspaceBoxIterator", "Convex subspace blockmap is not initialized"); -} - -int Map::mobjTouchedLineIterator(mobj_t *mo, int (*callback) (Line *, void *), - void *context) const { if(mo->lineRoot) { @@ -2452,8 +2403,8 @@ int Map::mobjTouchedLineIterator(mobj_t *mo, int (*callback) (Line *, void *), return false; // Continue iteration. } -int Map::mobjTouchedSectorIterator(mobj_t *mo, int (*callback) (Sector *, void *), - void *context) const +int Map::forAllSectorsTouchingMobj(mobj_t *mo, int (*callback) (Sector *, void *), + void *context) const { QVarLengthArray linkStore; @@ -2505,8 +2456,8 @@ int Map::mobjTouchedSectorIterator(mobj_t *mo, int (*callback) (Sector *, void * return false; // Continue iteration. } -int Map::lineTouchingMobjIterator(Line *line, int (*callback) (mobj_t *, void *), - void *context) const +int Map::forAllMobjsTouchingLine(Line *line, int (*callback) (mobj_t *, void *), + void *context) const { QVarLengthArray linkStore; @@ -2528,8 +2479,8 @@ int Map::lineTouchingMobjIterator(Line *line, int (*callback) (mobj_t *, void *) return false; // Continue iteration. } -int Map::sectorTouchingMobjIterator(Sector *sector, - int (*callback) (mobj_t *, void *), void *context) const +int Map::forAllMobjsTouchingSector(Sector *sector, int (*callback) (mobj_t *, void *), + void *context) const { QVarLengthArray linkStore; @@ -2545,7 +2496,7 @@ int Map::sectorTouchingMobjIterator(Sector *sector, // Collate mobjs linked to the sector's lines. linknode_t const *ln = d->lineNodes.nodes; - foreach(LineSide *side, sector->sides()) + for(LineSide *side : sector->sides()) { nodeindex_t root = d->lineLinks[side->line().indexInMap()]; @@ -2649,153 +2600,100 @@ void Map::link(Polyobj &polyobj) d->polyobjBlockmap->link(polyobj.aaBox, &polyobj); } -struct blockmapcellpolyobjsiterator_params_t -{ - int localValidCount; - int (*callback) (Polyobj *, void *); - void *context; -}; - -static int blockmapCellPolyobjsIterator(void *object, void *context) +LoopResult Map::forAllLinesInBox(AABoxd const &box, int flags, std::function func) const { - Polyobj *polyobj = static_cast(object); - blockmapcellpolyobjsiterator_params_t &parm = *static_cast(context); - - if(polyobj->validCount != parm.localValidCount) - { - // This polyobj has now been processed for the current iteration. - polyobj->validCount = parm.localValidCount; + LoopResult result = LoopContinue; - // Action the callback. - if(int result = parm.callback(polyobj, parm.context)) - return result; // Stop iteration. - } - - return false; // Continue iteration. -} - -int Map::polyobjBoxIterator(AABoxd const &box, - int (*callback) (struct polyobj_s *, void *), void *context) const -{ - if(!d->polyobjBlockmap.isNull()) - { - blockmapcellpolyobjsiterator_params_t parm; zap(parm); - parm.localValidCount = validCount; - parm.callback = callback; - parm.context = context; - - return d->polyobjBlockmap->iterate(box, blockmapCellPolyobjsIterator, &parm); - } - /// @throw MissingBlockmapError The polyobj blockmap is not yet initialized. - throw MissingBlockmapError("Map::polyobjBoxIterator", "Polyobj blockmap is not initialized"); -} - -struct polyobjlineiterator_params_t -{ - int (*callback) (Line *, void *); - void *context; -}; - -static int polyobjLineIterator(Polyobj *po, void *context = 0) -{ - polyobjlineiterator_params_t &parm = *static_cast(context); - - foreach(Line *line, po->lines()) - { - if(line->validCount() != validCount) - { - line->setValidCount(validCount); - - if(int result = parm.callback(line, parm.context)) - return result; - } - } - - return false; // Continue iteration. -} - -int Map::lineBoxIterator(AABoxd const &box, int flags, - int (*callback) (Line *, void *), void *context) const -{ // Process polyobj lines? if((flags & LIF_POLYOBJ) && polyobjCount()) { - if(d->polyobjBlockmap.isNull()) - /// @throw MissingBlockmapError The polyobj blockmap is not yet initialized. - throw MissingBlockmapError("Map::lineBoxIterator", "Polyobj blockmap is not initialized"); - - polyobjlineiterator_params_t pliParm; zap(pliParm); - pliParm.callback = callback; - pliParm.context = context; - - blockmapcellpolyobjsiterator_params_t parm; zap(parm); - parm.localValidCount = validCount; - parm.callback = polyobjLineIterator; - parm.context = &pliParm; - - if(int result = d->polyobjBlockmap->iterate(box, blockmapCellPolyobjsIterator, &parm)) - return result; + int const localValidCount = validCount; + result = polyobjBlockmap().forAllInBox(box, [&func, &localValidCount] (void *object) + { + Polyobj &pob = *(Polyobj *)object; + if(pob.validCount != localValidCount) // not yet processed + { + pob.validCount = localValidCount; + for(Line *line : pob.lines()) + { + if(line->validCount() != localValidCount) // not yet processed + { + line->setValidCount(localValidCount); + if(auto result = func(*line)) + return result; + } + } + } + return LoopResult(); // continue + }); } // Process sector lines? - if(flags & LIF_SECTOR) + if(!result && (flags & LIF_SECTOR)) { - if(d->lineBlockmap.isNull()) - /// @throw MissingBlockmapError The line blockmap is not yet initialized. - throw MissingBlockmapError("Map::lineBoxIterator", "Line blockmap is not initialized"); - - blockmapcelllinesiterator_params_t parm; zap(parm); - parm.localValidCount = validCount; - parm.callback = callback; - parm.context = context; - - if(int result = d->lineBlockmap->iterate(box, blockmapCellLinesIterator, &parm)) - return result; + int const localValidCount = validCount; + result = lineBlockmap().forAllInBox(box, [&func, &localValidCount] (void *object) + { + Line &line = *(Line *)object; + if(line.validCount() != localValidCount) // not yet processed + { + line.setValidCount(localValidCount); + return func(line); + } + return LoopResult(); // continue + }); } - return 0; // Continue iteration. + return result; } -int Map::linePathIterator(Vector2d const &from, Vector2d const &to, int flags, +int Map::forAllLinesInPath(Vector2d const &from, Vector2d const &to, int flags, int (*callback)(Line *, void *), void *context) const { + LoopResult result = LoopContinue; + // Process polyobj lines? if((flags & LIF_POLYOBJ) && polyobjCount()) { - if(d->polyobjBlockmap.isNull()) - /// @throw MissingBlockmapError The polyobj blockmap is not yet initialized. - throw MissingBlockmapError("Map::linePathIterator", "Polyobj blockmap is not initialized"); - - polyobjlineiterator_params_t pliParm; zap(pliParm); - pliParm.callback = callback; - pliParm.context = context; - - blockmapcellpolyobjsiterator_params_t parm; zap(parm); - parm.localValidCount = validCount; - parm.callback = polyobjLineIterator; - parm.context = &pliParm; + int const localValidCount = validCount; + result = polyobjBlockmap().forAllInPath(from, to, [&callback, &context, &localValidCount] (void *object) + { + Polyobj &pob = *(Polyobj *)object; + if(pob.validCount != localValidCount) // not yet processed + { + pob.validCount = localValidCount; + for(Line *line : pob.lines()) + { + if(line->validCount() != localValidCount) // not yet processed + { + line->setValidCount(localValidCount); - if(int result = d->polyobjBlockmap->iterate(from, to, blockmapCellPolyobjsIterator, &parm)) - return result; + if(int result = callback(line, context)) + return LoopResult( result ); + } + } + } + return LoopResult(); // continue + }); } // Process sector lines? - if(flags & LIF_SECTOR) + if(!result && (flags & LIF_SECTOR)) { - if(d->lineBlockmap.isNull()) - /// @throw MissingBlockmapError The line blockmap is not yet initialized. - throw MissingBlockmapError("Map::linePathIterator", "Line blockmap is not initialized"); - - blockmapcelllinesiterator_params_t parm; zap(parm); - parm.localValidCount = validCount; - parm.callback = callback; - parm.context = context; - - if(int result = d->lineBlockmap->iterate(from, to, blockmapCellLinesIterator, &parm)) - return result; + int const localValidCount = validCount; + result = lineBlockmap().forAllInPath(from, to, [&callback, &context, &localValidCount] (void *object) + { + Line &line = *(Line *)object; + if(line.validCount() != localValidCount) // not yet processed + { + line.setValidCount(localValidCount); + return LoopResult( callback(&line, context) ); + } + return LoopResult(); // continue + }); } - return 0; // Continue iteration. + return result; } BspLeaf &Map::bspLeafAt(Vector2d const &point) const @@ -3064,6 +2962,11 @@ int Map::generatorListIterator(uint listIndex, int (*callback) (Generator *, voi return 0; // Continue iteration. } +int Map::lumobjCount() const +{ + return d->lumobjs.count(); +} + Lumobj &Map::addLumobj(Lumobj const &lumobj) { d->lumobjs.append(new Lumobj(lumobj)); @@ -3088,7 +2991,7 @@ void Map::removeLumobj(int which) void Map::removeAllLumobjs() { - foreach(ConvexSubspace *subspace, d->subspaces) + for(ConvexSubspace *subspace : d->subspaces) { subspace->unlinkAllLumobjs(); } @@ -3096,9 +2999,34 @@ void Map::removeAllLumobjs() d->lumobjs.clear(); } -Map::Lumobjs const &Map::lumobjs() const +Lumobj &Map::lumobj(int index) const +{ + if(Lumobj *lum = lumobjPtr(index)) return *lum; + /// @throw MissingObjectError Invalid Lumobj reference specified. + throw MissingObjectError("Map::lumobj", "Unknown Lumobj index:" + String::number(index)); +} + +Lumobj *Map::lumobjPtr(int index) const +{ + if(index >= 0 && index < d->lumobjs.count()) + { + return d->lumobjs.at(index); + } + return nullptr; +} + +LoopResult Map::forAllLumobjs(std::function func) const +{ + for(Lumobj *lob : d->lumobjs) + { + if(auto result = func(*lob)) return result; + } + return LoopContinue; +} + +int Map::biasSourceCount() const { - return d->lumobjs; + return d->bias.sources.count(); } BiasSource &Map::addBiasSource(BiasSource const &biasSource) @@ -3109,7 +3037,7 @@ BiasSource &Map::addBiasSource(BiasSource const &biasSource) return *d->bias.sources.last(); } /// @throw FullError Attempt to add a new bias source when already at capcity. - throw FullError("Map::addBiasSource", QString("Already at maximum capacity (%1)").arg(MAX_BIAS_SOURCES)); + throw FullError("Map::addBiasSource", "Already at full capacity:" + String::number(MAX_BIAS_SOURCES)); } void Map::removeBiasSource(int which) @@ -3126,18 +3054,20 @@ void Map::removeAllBiasSources() d->bias.sources.clear(); } -Map::BiasSources const &Map::biasSources() const +BiasSource &Map::biasSource(int index) const { - return d->bias.sources; + if(BiasSource *bsrc = biasSourcePtr(index)) return *bsrc; + /// @throw MissingObjectError Invalid BiasSource reference specified. + throw MissingObjectError("Map::biasSource", "Unknown BiasSource index:" + String::number(index)); } -BiasSource *Map::biasSource(int index) const +BiasSource *Map::biasSourcePtr(int index) const { - if(index >= 0 && index < biasSourceCount()) + if(index >= 0 && index < d->bias.sources.count()) { - return biasSources().at(index); + return d->bias.sources.at(index); } - return 0; + return nullptr; } /** @@ -3148,7 +3078,7 @@ BiasSource *Map::biasSourceNear(Vector3d const &point) const { BiasSource *nearest = 0; coord_t minDist = 0; - foreach(BiasSource *src, d->bias.sources) + for(BiasSource *src : d->bias.sources) { coord_t dist = (src->origin() - point).length(); if(!nearest || dist < minDist) @@ -3160,9 +3090,18 @@ BiasSource *Map::biasSourceNear(Vector3d const &point) const return nearest; } -int Map::toIndex(BiasSource const &source) const +LoopResult Map::forAllBiasSources(std::function func) const +{ + for(BiasSource *bsrc : d->bias.sources) + { + if(auto result = func(*bsrc)) return result; + } + return LoopContinue; +} + +int Map::indexOf(BiasSource const &bsrc) const { - return d->bias.sources.indexOf(const_cast(&source)); + return d->bias.sources.indexOf(const_cast(&bsrc)); } uint Map::biasCurrentTime() const @@ -3183,28 +3122,28 @@ void Map::update() d->updateParticleGens(); // Defs might've changed. // Update all surfaces. - foreach(Sector *sector, d->sectors) - foreach(Plane *plane, sector->planes()) + for(Sector *sector : d->sectors) + for(Plane *plane : sector->planes()) { - plane->surface().markAsNeedingDecorationUpdate(); + plane->surface().markForDecorationUpdate(); } - foreach(Line *line, d->lines) + for(Line *line : d->lines) for(int i = 0; i < 2; ++i) { LineSide &side = line->side(i); if(!side.hasSections()) continue; - side.top().markAsNeedingDecorationUpdate(); - side.middle().markAsNeedingDecorationUpdate(); - side.bottom().markAsNeedingDecorationUpdate(); + side.top ().markForDecorationUpdate(); + side.middle().markForDecorationUpdate(); + side.bottom().markForDecorationUpdate(); } /// @todo Is this even necessary? - foreach(Polyobj *polyobj, d->polyobjs) - foreach(Line *line, polyobj->lines()) + for(Polyobj *polyobj : d->polyobjs) + for(Line *line : polyobj->lines()) { - line->front().middle().markAsNeedingDecorationUpdate(); + line->front().middle().markForDecorationUpdate(); } // Rebuild the surface material lists. @@ -3266,7 +3205,7 @@ void Map::update() #ifdef __CLIENT__ void Map::worldSystemFrameBegins(bool resetNextViewer) { - DENG2_ASSERT(&d->worldSys().map() == this); // Sanity check. + DENG2_ASSERT(&worldSys().map() == this); // Sanity check. // Interpolate the map ready for drawing view(s) of it. d->lerpTrackedPlanes(resetNextViewer); @@ -3290,27 +3229,27 @@ void Map::worldSystemFrameBegins(bool resetNextViewer) d->surfaceDecorator().redecorate(); // Generate lumobjs for all decorations who want them. - foreach(Line *line, d->lines) + for(Line *line : d->lines) for(int i = 0; i < 2; ++i) { LineSide &side = line->side(i); if(!side.hasSections()) continue; - d->generateLumobjs(side.middle().decorations()); - d->generateLumobjs(side.bottom().decorations()); - d->generateLumobjs(side.top().decorations()); + d->generateLumobjs(side.middle()); + d->generateLumobjs(side.bottom()); + d->generateLumobjs(side.top()); } - foreach(Sector *sector, d->sectors) - foreach(Plane *plane, sector->planes()) + for(Sector *sector : d->sectors) + for(Plane *plane : sector->planes()) { - d->generateLumobjs(plane->surface().decorations()); + d->generateLumobjs(plane->surface()); } } // Spawn omnilights for mobjs? if(useDynLights) { - foreach(Sector *sector, d->sectors) + for(Sector *sector : d->sectors) for(mobj_t *iter = sector->firstMobj(); iter; iter = iter->sNext) { Mobj_GenerateLumobjs(iter); @@ -3696,7 +3635,7 @@ static bool vertexHasValidLineOwnerRing(Vertex &v) * the lines which the vertex belongs to sorted by angle, (the rings are * arranged in clockwise order, east = 0). */ -void buildVertexLineOwnerRings(Map::Vertexes const &vertexes, Map::Lines &editableLines) +void buildVertexLineOwnerRings(QList const &vertexs, QList &editableLines) { LOG_AS("buildVertexLineOwnerRings"); @@ -3707,8 +3646,8 @@ void buildVertexLineOwnerRings(Map::Vertexes const &vertexes, Map::Lines &editab LineOwner *lineOwners = (LineOwner *) Z_Malloc(sizeof(LineOwner) * editableLines.count() * 2, PU_MAPSTATIC, 0); LineOwner *allocator = lineOwners; - foreach(Line *line, editableLines) - for(uint p = 0; p < 2; ++p) + for(Line *line : editableLines) + for(int p = 0; p < 2; ++p) { setVertexLineOwner(&line->vertex(p), line, &allocator); } @@ -3716,7 +3655,7 @@ void buildVertexLineOwnerRings(Map::Vertexes const &vertexes, Map::Lines &editab /* * Step 2: Sort line owners of each vertex and finalize the rings. */ - foreach(Vertex *v, vertexes) + for(Vertex *v : vertexs) { if(!v->_numLineOwners) continue; @@ -3775,22 +3714,14 @@ bool Map::isEditable() const struct VertexInfo { - /// Vertex for this info. - Vertex *vertex; - - /// Determined equivalent vertex. - Vertex *equiv; - - /// Line -> Vertex reference count. - uint refCount; - - VertexInfo() : vertex(0), equiv(0), refCount(0) - {} + Vertex *vertex = nullptr; ///< Vertex for this info. + Vertex *equiv = nullptr; ///< Determined equivalent vertex. + uint refCount = 0; ///< Line -> Vertex reference count. /// @todo Math here is not correct (rounding directionality). -ds int compareVertexOrigins(VertexInfo const &other) const { - DENG_ASSERT(vertex != 0 && other.vertex != 0); + DENG2_ASSERT(vertex && other.vertex); if(this == &other) return 0; if(vertex == other.vertex) return 0; @@ -3820,8 +3751,10 @@ void pruneVertexes(Mesh &mesh, Map::Lines const &lines) // Populate the vertex info. QVector vertexInfo(mesh.vertexCount()); int ord = 0; - foreach(Vertex *vertex, mesh.vertexes()) + for(Vertex *vertex : mesh.vertexs()) + { vertexInfo[ord++].vertex = vertex; + } { // Sort a copy to place near vertexes adjacently. @@ -3848,14 +3781,14 @@ void pruneVertexes(Mesh &mesh, Map::Lines const &lines) */ // Count line -> vertex references. - foreach(Line *line, lines) + for(Line *line : lines) { vertexInfo[line->from().indexInMap()].refCount++; vertexInfo[ line->to().indexInMap()].refCount++; } // Perform the replacement. - foreach(Line *line, lines) + for(Line *line : lines) { while(vertexInfo[line->from().indexInMap()].equiv) { @@ -3882,7 +3815,7 @@ void pruneVertexes(Mesh &mesh, Map::Lines const &lines) * Step 3 - Prune vertexes: */ int prunedCount = 0, numUnused = 0; - foreach(VertexInfo const &info, vertexInfo) + for(VertexInfo const &info : vertexInfo) { Vertex *vertex = info.vertex; @@ -3898,20 +3831,20 @@ void pruneVertexes(Mesh &mesh, Map::Lines const &lines) { // Re-index with a contiguous range of indices. int ord = 0; - foreach(Vertex *vertex, mesh.vertexes()) + for(Vertex *vertex : mesh.vertexs()) { vertex->setIndexInMap(ord++); } /// Update lines. @todo Line should handle this itself. - foreach(Line *line, lines) + for(Line *line : lines) { line->updateSlopeType(); line->updateAABox(); } - LOGDEV_MAP_NOTE("Pruned %d vertexes (%d equivalents, %d unused).") - << prunedCount << (prunedCount - numUnused) << numUnused; + LOGDEV_MAP_NOTE("Pruned %d vertexes (%d equivalents, %d unused)") + << prunedCount << (prunedCount - numUnused) << numUnused; } } @@ -3922,10 +3855,10 @@ bool Map::endEditing() d->editingEnabled = false; LOG_AS("Map"); - LOG_MAP_VERBOSE("Editing ended."); - LOGDEV_MAP_VERBOSE("New elements: %d Vertexes, %d Lines, %d Polyobjs and %d Sectors.") - << d->mesh.vertexCount() << d->editable.lines.count() - << d->editable.polyobjs.count() << d->editable.sectors.count(); + LOG_MAP_VERBOSE("Editing ended"); + LOGDEV_MAP_VERBOSE("New elements: %d Vertexes, %d Lines, %d Polyobjs and %d Sectors") + << d->mesh.vertexCount() << d->editable.lines.count() + << d->editable.polyobjs.count() << d->editable.sectors.count(); /* * Perform cleanup on the new map elements. @@ -3933,13 +3866,13 @@ bool Map::endEditing() pruneVertexes(d->mesh, d->editable.lines); // Ensure lines with only one sector are flagged as blocking. - foreach(Line *line, d->editable.lines) + for(Line *line : d->editable.lines) { if(!line->hasFrontSector() || !line->hasBackSector()) line->setFlags(DDLF_BLOCKING); } - buildVertexLineOwnerRings(d->mesh.vertexes(), d->editable.lines); + buildVertexLineOwnerRings(d->mesh.vertexs(), d->editable.lines); /* * Move the editable elements to the "static" element lists. @@ -3972,7 +3905,7 @@ bool Map::endEditing() Polyobj *polyobj = d->polyobjs.back(); // Create half-edge geometry and line segments for each line. - foreach(Line *line, polyobj->lines()) + for(Line *line : polyobj->lines()) { HEdge *hedge = polyobj->mesh().newHEdge(line->from()); @@ -4007,7 +3940,7 @@ bool Map::endEditing() d->initPolyobjBlockmap(); // Finish lines. - foreach(Line *line, d->lines) + for(Line *line : d->lines) for(int i = 0; i < 2; ++i) { line->side(i).updateSurfaceNormals(); @@ -4015,7 +3948,7 @@ bool Map::endEditing() } // Finish sectors. - foreach(Sector *sector, d->sectors) + for(Sector *sector : d->sectors) { d->buildClusters(*sector); sector->buildSides(); @@ -4023,8 +3956,8 @@ bool Map::endEditing() } // Finish planes. - foreach(Sector *sector, d->sectors) - foreach(Plane *plane, sector->planes()) + for(Sector *sector : d->sectors) + for(Plane *plane : sector->planes()) { plane->updateSoundEmitterOrigin(); } diff --git a/doomsday/client/src/world/plane.cpp b/doomsday/client/src/world/plane.cpp index c4e043175f..b8394e6fd2 100644 --- a/doomsday/client/src/world/plane.cpp +++ b/doomsday/client/src/world/plane.cpp @@ -1,7 +1,7 @@ /** @file plane.h World map plane. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -18,80 +18,64 @@ * 02110-1301 USA */ -#include "de_base.h" #include "world/plane.h" +#include #include "dd_loop.h" // frameTimePos #include "world/map.h" #include "world/thinkers.h" #include "world/worldsystem.h" /// ddMapSetup #include "Surface" #include "Sector" -#include using namespace de; DENG2_PIMPL(Plane) { - ThinkerT soundEmitter; - int indexInSector; ///< Index in the owning sector. - coord_t height; ///< Current @em sharp height. - coord_t targetHeight; ///< Target @em sharp height. - coord_t speed; ///< Movement speed (map space units per tic). Surface surface; + ThinkerT soundEmitter; -#ifdef __CLIENT__ - coord_t oldHeight[2]; ///< @em sharp height change tracking buffer (for smoothing). - coord_t heightSmoothed; ///< @ref height (smoothed). - coord_t heightSmoothedDelta; ///< Delta between the current @em sharp height and the visual height. - ClPlaneMover *mover; ///< The current mover. -#endif + int indexInSector = -1; ///< Index in the owning sector. + + coord_t height = 0; ///< Current @em sharp height. + coord_t targetHeight = 0; ///< Target @em sharp height. + coord_t speed = 0; ///< Movement speed (map space units per tic). - Instance(Public *i, coord_t height) - : Base(i) - , indexInSector(-1) - , height(height) - , targetHeight(height) - , speed(0) - , surface(dynamic_cast(*i)) #ifdef __CLIENT__ - , heightSmoothed(height) - , heightSmoothedDelta(0) - , mover(0) + coord_t oldHeight[2]; ///< @em sharp height change tracking buffer (for smoothing). + coord_t heightSmoothed = 0; ///< @ref height (smoothed). + coord_t heightSmoothedDelta = 0; ///< Delta between the current @em sharp height and the visual height. + ClPlaneMover *mover = nullptr; ///< The current mover. #endif + + Instance(Public *i) : Base(i), surface(dynamic_cast(*i)) { #ifdef __CLIENT__ - oldHeight[0] = oldHeight[1] = height; + de::zap(oldHeight); #endif } ~Instance() { - DENG2_FOR_PUBLIC_AUDIENCE(Deletion, i) i->planeBeingDeleted(self); + DENG2_FOR_PUBLIC_AUDIENCE2(Deletion, i) i->planeBeingDeleted(self); #ifdef __CLIENT__ // Stop movement tracking of this plane. - self.map().trackedPlanes().remove(&self); + map().trackedPlanes().remove(&self); #endif } - void notifyHeightChanged() + inline Map &map() const { return self.map(); } + + void setHeight(coord_t newHeight) { - DENG2_FOR_PUBLIC_AUDIENCE(HeightChange, i) - { - i->planeHeightChanged(self); - } - } + height = targetHeight = newHeight; #ifdef __CLIENT__ - void notifySmoothedHeightChanged() - { - DENG2_FOR_PUBLIC_AUDIENCE(HeightSmoothedChange, i) - { - i->planeHeightSmoothedChanged(self); - } - } + heightSmoothed = newHeight; + oldHeight[0] = oldHeight[1] = newHeight; #endif + } void applySharpHeightChange(coord_t newHeight) { @@ -111,7 +95,7 @@ DENG2_PIMPL(Plane) /// @todo optimize: Translation on the world up axis would be a /// trivial operation to perform, which, would not require plotting /// decorations again. This frequent case should be designed for. - surface.markAsNeedingDecorationUpdate(); + surface.markForDecorationUpdate(); #endif } @@ -121,7 +105,7 @@ DENG2_PIMPL(Plane) if(!ddMapSetup) { // Add ourself to tracked plane list (for movement interpolation). - self.map().trackedPlanes().insert(&self); + map().trackedPlanes().insert(&self); } #endif } @@ -150,16 +134,41 @@ DENG2_PIMPL(Plane) findgeneratorworker_params_t parm; parm.plane = thisPublic; parm.found = 0; - self.map().generatorIterator(findGeneratorWorker, &parm); + map().generatorIterator(findGeneratorWorker, &parm); return parm.found; } #endif + + void notifyHeightChanged() + { + DENG2_FOR_PUBLIC_AUDIENCE2(HeightChange, i) i->planeHeightChanged(self); + } + +#ifdef __CLIENT__ + void notifySmoothedHeightChanged() + { + DENG2_FOR_PUBLIC_AUDIENCE2(HeightSmoothedChange, i) i->planeHeightSmoothedChanged(self); + } +#endif + + DENG2_PIMPL_AUDIENCE(Deletion) + DENG2_PIMPL_AUDIENCE(HeightChange) +#ifdef __CLIENT__ + DENG2_PIMPL_AUDIENCE(HeightSmoothedChange) +#endif }; +DENG2_AUDIENCE_METHOD(Plane, Deletion) +DENG2_AUDIENCE_METHOD(Plane, HeightChange) +#ifdef __CLIENT__ +DENG2_AUDIENCE_METHOD(Plane, HeightSmoothedChange) +#endif + Plane::Plane(Sector §or, Vector3f const &normal, coord_t height) : MapElement(DMU_PLANE, §or) - , d(new Instance(this, height)) + , d(new Instance(this)) { + d->setHeight(height); setNormal(normal); } @@ -265,7 +274,7 @@ void Plane::lerpSmoothedHeight() { d->heightSmoothed = newHeightSmoothed; d->notifySmoothedHeightChanged(); - d->surface.markAsNeedingDecorationUpdate(); + d->surface.markForDecorationUpdate(); } } @@ -279,7 +288,7 @@ void Plane::resetSmoothedHeight() { d->heightSmoothed = newHeightSmoothed; d->notifySmoothedHeightChanged(); - d->surface.markAsNeedingDecorationUpdate(); + d->surface.markForDecorationUpdate(); } } @@ -295,7 +304,7 @@ void Plane::updateHeightTracking() // Too fast: make an instantaneous jump. d->oldHeight[0] = d->oldHeight[1]; } - d->surface.markAsNeedingDecorationUpdate(); + d->surface.markForDecorationUpdate(); } } diff --git a/doomsday/client/src/world/sector.cpp b/doomsday/client/src/world/sector.cpp index 501309f648..2ad093a4a5 100644 --- a/doomsday/client/src/world/sector.cpp +++ b/doomsday/client/src/world/sector.cpp @@ -307,30 +307,34 @@ void Sector::buildSides() #ifdef DENG2_QT_4_7_OR_NEWER int count = 0; - for(Line *line : map().lines()) + map().forAllLines([this, &count] (Line &line) { - if(line->frontSectorPtr() == this || line->backSectorPtr() == this) + if(line.frontSectorPtr() == this || line.backSectorPtr() == this) + { ++count; - } + } + return LoopContinue; + }); if(!count) return; d->sides.reserve(count); #endif - for(Line *line : map().lines()) + map().forAllLines([this] (Line &line) { - if(line->frontSectorPtr() == this) + if(line.frontSectorPtr() == this) { // Ownership of the side is not given to the sector. - d->sides.append(&line->front()); + d->sides.append(&line.front()); } - else if(line->backSectorPtr() == this) + else if(line.backSectorPtr() == this) { // Ownership of the side is not given to the sector. - d->sides.append(&line->back()); + d->sides.append(&line.back()); } - } + return LoopContinue; + }); } Plane *Sector::addPlane(Vector3f const &normal, coord_t height) @@ -344,7 +348,7 @@ Plane *Sector::addPlane(Vector3f const &normal, coord_t height) { // We want notification of height changes so that we can update sound // emitter origins of dependent surfaces. - plane->audienceForHeightChange += d; + plane->audienceForHeightChange() += d; } // Once both floor and ceiling are known we can determine the z-height origin diff --git a/doomsday/client/src/world/sectorcluster.cpp b/doomsday/client/src/world/sectorcluster.cpp index bea6401e91..5e144ffda5 100644 --- a/doomsday/client/src/world/sectorcluster.cpp +++ b/doomsday/client/src/world/sectorcluster.cpp @@ -27,6 +27,9 @@ #include "Line" #include "Plane" #include "Surface" +#ifdef __CLIENT__ +# include "world/blockmap.h" +#endif #include "world/map.h" #include "world/p_object.h" #include "world/p_players.h" @@ -214,21 +217,21 @@ DENG2_PIMPL(SectorCluster) if(yes) { - plane->audienceForDeletion += this; + plane->audienceForDeletion() += this; if(observeHeight) { - plane->audienceForHeightChange += this; + plane->audienceForHeightChange() += this; #ifdef __CLIENT__ - plane->audienceForHeightSmoothedChange += this; + plane->audienceForHeightSmoothedChange() += this; #endif } } else { - plane->audienceForDeletion -= this; - plane->audienceForHeightChange -= this; + plane->audienceForDeletion() -= this; + plane->audienceForHeightChange() -= this; #ifdef __CLIENT__ - plane->audienceForHeightSmoothedChange -= this; + plane->audienceForHeightSmoothedChange() -= this; #endif } } @@ -609,17 +612,17 @@ DENG2_PIMPL(SectorCluster) LineSide &front = line.front(); DENG2_ASSERT(front.hasSections()); { - front.middle().markAsNeedingDecorationUpdate(); - front.bottom().markAsNeedingDecorationUpdate(); - front. top().markAsNeedingDecorationUpdate(); + front.middle().markForDecorationUpdate(); + front.bottom().markForDecorationUpdate(); + front. top().markForDecorationUpdate(); } LineSide &back = line.back(); if(back.hasSections()) { - back.middle().markAsNeedingDecorationUpdate(); - back.bottom().markAsNeedingDecorationUpdate(); - back .top().markAsNeedingDecorationUpdate(); + back.middle().markForDecorationUpdate(); + back.bottom().markForDecorationUpdate(); + back .top().markForDecorationUpdate(); } } @@ -814,12 +817,6 @@ DENG2_PIMPL(SectorCluster) reverbSubspaces.insert(subspace); } - static int addReverbSubspaceWorker(ConvexSubspace *subspace, void *context) - { - static_cast(context)->addReverbSubspace(subspace); - return false; // Continue iteration. - } - /** * Perform environmental audio (reverb) initialization. * @@ -832,15 +829,35 @@ DENG2_PIMPL(SectorCluster) */ void findReverbSubspaces() { - AABoxd affectionBounds = self.aaBox(); - affectionBounds.minX -= 128; - affectionBounds.minY -= 128; - affectionBounds.maxX += 128; - affectionBounds.maxY += 128; + Map const &map = self.sector().map(); + + AABoxd box = self.aaBox(); + box.minX -= 128; + box.minY -= 128; + box.maxX += 128; + box.maxY += 128; // Link all convex subspaces whose axis-aligned bounding box intersects // with the affection bounds to the reverb set. - self.sector().map().subspaceBoxIterator(affectionBounds, addReverbSubspaceWorker, this); + int const localValidCount = ++validCount; + map.subspaceBlockmap().forAllInBox(box, [this, &box, &localValidCount] (void *object) + { + ConvexSubspace &sub = *(ConvexSubspace *)object; + if(sub.validCount() != localValidCount) // not yet processed + { + sub.setValidCount(localValidCount); + // Check the bounds. + AABoxd const &polyBox = sub.poly().aaBox(); + if(!(polyBox.maxX < box.minX || + polyBox.minX > box.maxX || + polyBox.minY > box.maxY || + polyBox.maxY < box.minY)) + { + addReverbSubspace(&sub); + } + } + return LoopContinue; + }); } /** @@ -1231,7 +1248,7 @@ bool SectorCluster::updateBiasContributors(Shard *shard) { if(Instance::GeometryData *gdata = d->geomDataForShard(shard)) { - Map::BiasSources const &sources = sector().map().biasSources(); + Map const &map = sector().map(); BiasTracker &tracker = shard->biasTracker(); tracker.clearContributors(); @@ -1243,15 +1260,15 @@ bool SectorCluster::updateBiasContributors(Shard *shard) Plane const &plane = visPlane(gdata->geomId); Surface const &surface = plane.surface(); - Vector3d surfacePoint(subspace.poly().center(), plane.heightSmoothed()); + Vector3d const surfacePoint(subspace.poly().center(), plane.heightSmoothed()); - foreach(BiasSource *source, sources) + map.forAllBiasSources([&tracker, &subspace, &surface, &surfacePoint] (BiasSource &source) { // If the source is too weak we will ignore it completely. - if(source->intensity() <= 0) - continue; + if(source.intensity() <= 0) + return LoopContinue; - Vector3d sourceToSurface = (source->origin() - surfacePoint).normalize(); + Vector3d sourceToSurface = (source.origin() - surfacePoint).normalize(); coord_t distance = 0; // Calculate minimum 2D distance to the subspace. @@ -1260,16 +1277,17 @@ bool SectorCluster::updateBiasContributors(Shard *shard) HEdge *node = baseNode; do { - coord_t len = (Vector2d(source->origin()) - node->origin()).length(); + coord_t len = (Vector2d(source.origin()) - node->origin()).length(); if(node == baseNode || len < distance) distance = len; } while((node = &node->next()) != baseNode); if(sourceToSurface.dot(surface.normal()) < 0) - continue; + return LoopContinue; - tracker.addContributor(source, source->evaluateIntensity() / de::max(distance, 1.0)); - } + tracker.addContributor(&source, source.evaluateIntensity() / de::max(distance, 1.0)); + return LoopContinue; + }); break; } case DMU_SEGMENT: { @@ -1279,28 +1297,29 @@ bool SectorCluster::updateBiasContributors(Shard *shard) Vector2d const &to = seg.hedge().twin().origin(); Vector2d const center = (from + to) / 2; - foreach(BiasSource *source, sources) + map.forAllBiasSources([&tracker, &surface, &from, &to, ¢er] (BiasSource &source) { // If the source is too weak we will ignore it completely. - if(source->intensity() <= 0) - continue; + if(source.intensity() <= 0) + return LoopContinue; - Vector3d sourceToSurface = (source->origin() - center).normalize(); + Vector3d sourceToSurface = (source.origin() - center).normalize(); // Calculate minimum 2D distance to the segment. coord_t distance = 0; for(int k = 0; k < 2; ++k) { - coord_t len = (Vector2d(source->origin()) - (!k? from : to)).length(); + coord_t len = (Vector2d(source.origin()) - (!k? from : to)).length(); if(k == 0 || len < distance) distance = len; } if(sourceToSurface.dot(surface.normal()) < 0) - continue; + return LoopContinue; - tracker.addContributor(source, source->evaluateIntensity() / de::max(distance, 1.0)); - } + tracker.addContributor(&source, source.evaluateIntensity() / de::max(distance, 1.0)); + return LoopContinue; + }); break; } default: diff --git a/doomsday/client/src/world/surface.cpp b/doomsday/client/src/world/surface.cpp index 43b0f12ebc..24a1a9f1ad 100644 --- a/doomsday/client/src/world/surface.cpp +++ b/doomsday/client/src/world/surface.cpp @@ -1,7 +1,7 @@ /** @file surface.cpp World map surface. * * @authors Copyright © 2003-2013 Jaakko Keränen - * @authors Copyright © 2006-2013 Daniel Swanson + * @authors Copyright © 2006-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -18,9 +18,14 @@ * 02110-1301 USA */ -#include "de_base.h" #include "world/surface.h" +#ifdef __CLIENT__ +# include +#endif +#include +#include +#include #include "de_defs.h" // Def_GetGenerator #include "dd_loop.h" // frameTimePos @@ -40,130 +45,120 @@ # include "render/rend_main.h" #endif -#include -#include -#include - using namespace de; DENG2_PIMPL(Surface) { - Matrix3f tangentMatrix; ///< Tangent space vectors. - bool needUpdateTangentMatrix; ///< @c true= marked for update. - Material *material; ///< Currently bound material. - bool materialIsMissingFix; ///< @c true= @ref material is a "missing fix". - Vector2f materialOrigin; ///< @em sharp surface space material origin. + int flags = 0; ///< @ref sufFlags + + Matrix3f tangentMatrix { Matrix3f::Zero }; ///< Tangent space vectors. + bool needUpdateTangentMatrix = false; ///< @c true= marked for update. + + Material *material = nullptr; ///< Currently bound material. + bool materialIsMissingFix = false; ///< @c true= @ref material is a "missing fix". + Vector2f materialOrigin; ///< @em sharp surface space material origin. + Vector3f tintColor; - float opacity; - blendmode_t blendMode; - int flags; ///< @ref sufFlags + float opacity = 0; + blendmode_t blendMode { BM_NORMAL }; #ifdef __CLIENT__ - Decorations decorations; ///< Surface (light) decorations (owned). + typedef QList Decorations; + Decorations decorations; ///< Surface (light) decorations (owned). + bool needDecorationUpdate = true; ///< @c true= An update is needed. - Vector2f oldMaterialOrigin[2]; ///< Old @em sharp surface space material origins, for smoothing. - Vector2f materialOriginSmoothed; ///< @em smoothed surface space material origin. - Vector2f materialOriginSmoothedDelta; ///< Delta between @em sharp and @em smoothed. + Vector2f oldMaterialOrigin[2]; ///< Old @em sharp surface space material origins, for smoothing. + Vector2f materialOriginSmoothed; ///< @em smoothed surface space material origin. + Vector2f materialOriginSmoothedDelta; ///< Delta between @em sharp and @em smoothed. #endif - Instance(Public *i) - : Base(i) - , tangentMatrix(Matrix3f::Zero) - , needUpdateTangentMatrix(false) - , material(0) - , materialIsMissingFix(false) - , opacity(0) - , blendMode(BM_NORMAL) - , flags(0) + Instance(Public *i) : Base(i) {} ~Instance() { #ifdef __CLIENT__ // Stop scroll interpolation for this surface. - self.map().scrollingSurfaces().remove(&self); + map().scrollingSurfaces().remove(&self); // Stop material redecoration for this surface. - self.map().unlinkInMaterialLists(&self); + map().unlinkInMaterialLists(&self); qDeleteAll(decorations); #endif } + inline Map &map() const { return self.map(); } + inline MapElement &parent() const { return self.parent(); } + #ifdef DENG_DEBUG - inline bool isSideMiddle() + inline bool isSideMiddle() const { - return self.parent().type() == DMU_SIDE && - &self == &self.parent().as().middle(); + return parent().type() == DMU_SIDE && + &self == &parent().as().middle(); } - inline bool isSectorExtraPlane() + inline bool isSectorExtraPlane() const { - if(self.parent().type() != DMU_PLANE) return false; - Plane const &plane = self.parent().as(); + if(parent().type() != DMU_PLANE) return false; + auto const &plane = parent().as(); return !(plane.isSectorFloor() || plane.isSectorCeiling()); } #endif - void notifyNormalChanged() + void updateTangentMatrix() { - DENG2_FOR_PUBLIC_AUDIENCE(NormalChange, i) - { - i->surfaceNormalChanged(self); - } + needUpdateTangentMatrix = false; + + dfloat values[9]; + Vector3f normal = tangentMatrix.column(2); + V3f_Set(values + 6, normal.x, normal.y, normal.z); + V3f_BuildTangents(values, values + 3, values + 6); + + tangentMatrix = Matrix3f(values); } void notifyMaterialOriginChanged() { - DENG2_FOR_PUBLIC_AUDIENCE(MaterialOriginChange, i) - { - i->surfaceMaterialOriginChanged(self); - } + DENG2_FOR_PUBLIC_AUDIENCE2(MaterialOriginChange, i) i->surfaceMaterialOriginChanged(self); #ifdef __CLIENT__ if(!ddMapSetup) { - self._needDecorationUpdate = true; - - self.map().scrollingSurfaces().insert(&self); + needDecorationUpdate = true; + map().scrollingSurfaces().insert(&self); } #endif } - void notifyOpacityChanged() + void notifyNormalChanged() { - DENG2_FOR_PUBLIC_AUDIENCE(OpacityChange, i) - { - i->surfaceOpacityChanged(self); - } + DENG2_FOR_PUBLIC_AUDIENCE2(NormalChange, i) i->surfaceNormalChanged(self); } - void notifyTintColorChanged() + void notifyOpacityChanged() { - DENG2_FOR_PUBLIC_AUDIENCE(TintColorChange, i) - { - i->surfaceTintColorChanged(self); - } + DENG2_FOR_PUBLIC_AUDIENCE2(OpacityChange, i) i->surfaceOpacityChanged(self); } - void updateTangentMatrix() + void notifyTintColorChanged() { - needUpdateTangentMatrix = false; - - dfloat values[9]; - Vector3f normal = tangentMatrix.column(2); - V3f_Set(values + 6, normal.x, normal.y, normal.z); - V3f_BuildTangents(values, values + 3, values + 6); - - tangentMatrix = Matrix3f(values); + DENG2_FOR_PUBLIC_AUDIENCE2(TintColorChange, i) i->surfaceTintColorChanged(self); } + + DENG2_PIMPL_AUDIENCE(MaterialOriginChange) + DENG2_PIMPL_AUDIENCE(NormalChange) + DENG2_PIMPL_AUDIENCE(OpacityChange) + DENG2_PIMPL_AUDIENCE(TintColorChange) }; +DENG2_AUDIENCE_METHOD(Surface, MaterialOriginChange) +DENG2_AUDIENCE_METHOD(Surface, NormalChange) +DENG2_AUDIENCE_METHOD(Surface, OpacityChange) +DENG2_AUDIENCE_METHOD(Surface, TintColorChange) + Surface::Surface(MapElement &owner, float opacity, Vector3f const &tintColor) : MapElement(DMU_SURFACE, &owner) -#ifdef __CLIENT__ - , _needDecorationUpdate(true) -#endif , d(new Instance(this)) { d->opacity = opacity; @@ -182,17 +177,17 @@ Matrix3f const &Surface::tangentMatrix() const Surface &Surface::setNormal(Vector3f const &newNormal) { - Vector3f oldNormal = normal(); - Vector3f newNormalNormalized = newNormal.normalize(); + Vector3f const oldNormal = normal(); + Vector3f const newNormalNormalized = newNormal.normalize(); if(oldNormal != newNormalNormalized) { - d->tangentMatrix.at(0, 2) = newNormalNormalized.x; - d->tangentMatrix.at(1, 2) = newNormalNormalized.y; - d->tangentMatrix.at(2, 2) = newNormalNormalized.z; + for(int i = 0; i < 3; ++i) + { + d->tangentMatrix.at(i, 2) = newNormalNormalized[i]; + } // We'll need to recalculate the tangents when next referenced. d->needUpdateTangentMatrix = true; - d->notifyNormalChanged(); } return *this; @@ -211,12 +206,12 @@ Surface &Surface::setFlags(int flagsToChange, FlagOp operation) bool Surface::hasMaterial() const { - return d->material != 0; + return d->material != nullptr; } bool Surface::hasFixMaterial() const { - return d->material != 0 && d->materialIsMissingFix; + return hasMaterial() && d->materialIsMissingFix; } Material &Surface::material() const @@ -255,7 +250,7 @@ Surface &Surface::setMaterial(Material *newMaterial, bool isMissingFix) #ifdef __CLIENT__ // When the material changes any existing decorations are cleared. clearDecorations(); - _needDecorationUpdate = true; + d->needDecorationUpdate = true; if(!ddMapSetup) { @@ -293,8 +288,8 @@ Surface &Surface::setMaterialOrigin(Vector2f const &newOrigin) // During map setup we'll apply this immediately to the visual origin also. if(ddMapSetup) { - d->materialOriginSmoothed = d->materialOrigin; - d->materialOriginSmoothedDelta.x = d->materialOriginSmoothedDelta.y = 0; + d->materialOriginSmoothed = d->materialOrigin; + d->materialOriginSmoothedDelta = Vector2f(); d->oldMaterialOrigin[0] = d->oldMaterialOrigin[1] = d->materialOrigin; } @@ -333,7 +328,7 @@ void Surface::lerpSmoothedMaterialOrigin() d->materialOriginSmoothed = d->materialOrigin + d->materialOriginSmoothedDelta; #ifdef __CLIENT__ - markAsNeedingDecorationUpdate(); + markForDecorationUpdate(); #endif } @@ -341,10 +336,10 @@ void Surface::resetSmoothedMaterialOrigin() { // $smoothmaterialorigin d->materialOriginSmoothed = d->oldMaterialOrigin[0] = d->oldMaterialOrigin[1] = d->materialOrigin; - d->materialOriginSmoothedDelta.x = d->materialOriginSmoothedDelta.y = 0; + d->materialOriginSmoothedDelta = Vector2f(); #ifdef __CLIENT__ - markAsNeedingDecorationUpdate(); + markForDecorationUpdate(); #endif } @@ -393,9 +388,9 @@ Vector3f const &Surface::tintColor() const Surface &Surface::setTintColor(Vector3f const &newTintColor) { - Vector3f newColorClamped(de::clamp(0.f, newTintColor.x, 1.f), - de::clamp(0.f, newTintColor.y, 1.f), - de::clamp(0.f, newTintColor.z, 1.f)); + Vector3f const newColorClamped(de::clamp(0.f, newTintColor.x, 1.f), + de::clamp(0.f, newTintColor.y, 1.f), + de::clamp(0.f, newTintColor.z, 1.f)); if(d->tintColor != newColorClamped) { @@ -448,9 +443,13 @@ void Surface::clearDecorations() d->decorations.clear(); } -Surface::Decorations const &Surface::decorations() const +LoopResult Surface::forAllDecorations(std::function func) const { - return d->decorations; + for(Decoration *decor : d->decorations) + { + if(auto result = func(*decor)) return result; + } + return LoopContinue; } int Surface::decorationCount() const @@ -458,12 +457,17 @@ int Surface::decorationCount() const return d->decorations.count(); } -void Surface::markAsNeedingDecorationUpdate() +void Surface::markForDecorationUpdate(bool yes) { if(ddMapSetup) return; + d->needDecorationUpdate = yes; +} - _needDecorationUpdate = true; +bool Surface::needsDecorationUpdate() const +{ + return d->needDecorationUpdate; } + #endif // __CLIENT__ int Surface::property(DmuArgs &args) const @@ -471,7 +475,7 @@ int Surface::property(DmuArgs &args) const switch(args.prop) { case DMU_MATERIAL: { - Material *mat = d->materialIsMissingFix? 0 : d->material; + Material *mat = (d->materialIsMissingFix? nullptr : d->material); args.setValue(DMT_SURFACE_MATERIAL, &mat, 0); break; } diff --git a/doomsday/client/src/world/worldsystem.cpp b/doomsday/client/src/world/worldsystem.cpp index e118269c59..cd503f8aaf 100644 --- a/doomsday/client/src/world/worldsystem.cpp +++ b/doomsday/client/src/world/worldsystem.cpp @@ -18,24 +18,40 @@ * 02110-1301 USA */ -#include "de_platform.h" #include "world/worldsystem.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "de_defs.h" #include "de_play.h" #include "de_filesys.h" #include "dd_main.h" #include "dd_def.h" #include "dd_loop.h" -#include "ui/progress.h" #include "audio/s_main.h" -#include "edit_map.h" #include "network/net_main.h" +#include "edit_map.h" #include "Plane" #include "Sector" #include "SectorCluster" +#include "world/p_ticker.h" +#include "world/sky.h" +#include "world/thinkers.h" + +#include "ui/progress.h" #ifdef __CLIENT__ # include "clientapp.h" @@ -61,23 +77,6 @@ # include "server/sv_pool.h" #endif -#include "world/p_ticker.h" -#include "world/sky.h" -#include "world/thinkers.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - using namespace de; int validCount = 1; // Increment every time a check is made. @@ -252,7 +251,7 @@ class MapConversionReporter } } - Map *_map; ///< Map currently being reported on, if any (not owned). + Map *_map = nullptr; ///< Map currently being reported on, if any (not owned). UnclosedSectorMap _unclosedSectors; OneWayWindowMap _oneWayWindows; }; @@ -294,14 +293,6 @@ DENG2_PIMPL(WorldSystem) Instance(Public *i) : Base(i) {} - void notifyMapChange() - { - DENG2_FOR_PUBLIC_AUDIENCE(MapChange, i) - { - i->worldSystemMapChanged(); - } - } - /** * Compose the relative path (relative to the runtime directory) to the * directory of the cache where maps from this source (e.g., the add-on @@ -457,7 +448,7 @@ DENG2_PIMPL(WorldSystem) /// so that it may perform the connection itself. Such notification /// would also afford the map the opportunity to prepare various data /// which is only needed when made current (e.g., caches for render). - self.audienceForFrameBegin += map; + self.audienceForFrameBegin() += map; #endif // Print summary information about this map. @@ -581,11 +572,14 @@ DENG2_PIMPL(WorldSystem) #ifdef __CLIENT__ /// @todo Refactor away: - foreach(Sector *sector, map->sectors()) - foreach(LineSide *side, sector->sides()) + map->forAllSectors([] (Sector §or) { - side->fixMissingMaterials(); - } + for(LineSide *side : sector.sides()) + { + side->fixMissingMaterials(); + } + return LoopContinue; + }); #endif map->initPolyobjs(); @@ -689,7 +683,7 @@ DENG2_PIMPL(WorldSystem) { // Remove the current map from our audiences. /// @todo Map should handle this. - self.audienceForFrameBegin -= map; + self.audienceForFrameBegin() -= map; } #endif @@ -767,8 +761,25 @@ DENG2_PIMPL(WorldSystem) hand->setOrigin(viewData->current.origin + viewData->frontVec.xzy() * handDistance); } #endif + + void notifyMapChange() + { + DENG2_FOR_PUBLIC_AUDIENCE2(MapChange, i) i->worldSystemMapChanged(); + } + + DENG2_PIMPL_AUDIENCE(MapChange) +#ifdef __CLIENT__ + DENG2_PIMPL_AUDIENCE(FrameBegin) + DENG2_PIMPL_AUDIENCE(FrameEnd) +#endif }; +DENG2_AUDIENCE_METHOD(WorldSystem, MapChange) +#ifdef __CLIENT__ +DENG2_AUDIENCE_METHOD(WorldSystem, FrameBegin) +DENG2_AUDIENCE_METHOD(WorldSystem, FrameEnd) +#endif + WorldSystem::WorldSystem() : d(new Instance(this)) {} @@ -827,7 +838,7 @@ void WorldSystem::reset() { for(int i = 0; i < DDMAXPLAYERS; ++i) { - player_t *plr = &ddPlayers[i]; + player_t *plr = &ddPlayers[i]; ddplayer_t *ddpl = &plr->shared; // Mobjs go down with the map. @@ -858,7 +869,7 @@ void WorldSystem::update() { for(int i = 0; i < DDMAXPLAYERS; ++i) { - player_t *plr = &ddPlayers[i]; + player_t *plr = &ddPlayers[i]; ddplayer_t *ddpl = &plr->shared; // States have changed, the state pointers are unknown. @@ -913,7 +924,7 @@ Hand &WorldSystem::hand(coord_t *distance) const if(!d->hand) { d->hand.reset(new Hand()); - audienceForFrameEnd += *d->hand; + audienceForFrameEnd() += *d->hand; if(d->map) { d->updateHandOrigin(); @@ -929,7 +940,7 @@ Hand &WorldSystem::hand(coord_t *distance) const void WorldSystem::beginFrame(bool resetNextViewer) { // Notify interested parties that a new frame has begun. - DENG2_FOR_AUDIENCE(FrameBegin, i) i->worldSystemFrameBegins(resetNextViewer); + DENG2_FOR_AUDIENCE2(FrameBegin, i) i->worldSystemFrameBegins(resetNextViewer); } void WorldSystem::endFrame() @@ -947,7 +958,7 @@ void WorldSystem::endFrame() } // Notify interested parties that the current frame has ended. - DENG2_FOR_AUDIENCE(FrameEnd, i) i->worldSystemFrameEnds(); + DENG2_FOR_AUDIENCE2(FrameEnd, i) i->worldSystemFrameEnds(); } bool WorldSystem::isPointInVoid(Vector3d const &pos) const diff --git a/doomsday/server/src/server/sv_pool.cpp b/doomsday/server/src/server/sv_pool.cpp index cbcabdbf3e..3a133f9198 100644 --- a/doomsday/server/src/server/sv_pool.cpp +++ b/doomsday/server/src/server/sv_pool.cpp @@ -413,21 +413,24 @@ void Sv_RegisterPlayer(dt_player_t* reg, uint number) * Store the state of the sector into the register-sector. * Called at register init and after each delta generation. * - * @param reg The sector register to be initialized. - * @param number The world sector number to be registered. + * @param reg The sector register to be initialized. + * @param number The world sector number to be registered. */ void Sv_RegisterSector(dt_sector_t *reg, int number) { - Sector *sector = App_WorldSystem().map().sectors().at(number); + DENG2_ASSERT(reg); + Sector §or = App_WorldSystem().map().sector(number); - reg->lightLevel = sector->lightLevel(); + reg->lightLevel = sector.lightLevel(); for(int i = 0; i < 3; ++i) - reg->rgb[i] = sector->lightColor()[i]; + { + reg->rgb[i] = sector.lightColor()[i]; + } // @todo $nplanes for(int i = 0; i < 2; ++i) // number of planes in sector. { - Plane const &plane = sector->plane(i); + Plane const &plane = sector.plane(i); // Plane properties reg->planes[i].height = plane.height(); @@ -456,7 +459,7 @@ void Sv_RegisterSide(dt_side_t *reg, int number) { DENG2_ASSERT(reg != 0); - LineSide *side = App_WorldSystem().map().sideByIndex(number); + LineSide *side = App_WorldSystem().map().sidePtr(number); if(side->hasSections()) { @@ -486,15 +489,14 @@ void Sv_RegisterSide(dt_side_t *reg, int number) */ void Sv_RegisterPoly(dt_poly_t *reg, uint number) { - DENG_ASSERT(reg != 0); + DENG_ASSERT(reg); + Polyobj const &pob = App_WorldSystem().map().polyobj(number); - Polyobj *poly = App_WorldSystem().map().polyobjs().at(number); - - reg->dest[VX] = poly->dest[VX]; - reg->dest[VY] = poly->dest[VY]; - reg->speed = poly->speed; - reg->destAngle = poly->destAngle; - reg->angleSpeed = poly->angleSpeed; + reg->dest[VX] = pob.dest[VX]; + reg->dest[VY] = pob.dest[VY]; + reg->speed = pob.speed; + reg->destAngle = pob.destAngle; + reg->angleSpeed = pob.angleSpeed; } /** @@ -624,41 +626,41 @@ dd_bool Sv_RegisterComparePlayer(cregister_t* reg, uint number, } /** - * @return @c true, if the result is not void. + * @return @c true if the result is not void. */ -dd_bool Sv_RegisterCompareSector(cregister_t *reg, int number, - sectordelta_t *d, byte doUpdate) +dd_bool Sv_RegisterCompareSector(cregister_t *reg, int number, sectordelta_t *d, byte doUpdate) { - dt_sector_t *r = ®->sectors[number]; - Sector const *s = App_WorldSystem().map().sectors().at(number); + DENG2_ASSERT(reg && d); + dt_sector_t *r = ®->sectors[number]; + Sector const &s = App_WorldSystem().map().sector(number); int df = 0; // Determine which data is different. - if(s->floorSurface().materialPtr() != r->planes[PLN_FLOOR].surface.material) + if(s.floorSurface().materialPtr() != r->planes[PLN_FLOOR].surface.material) df |= SDF_FLOOR_MATERIAL; - if(s->ceilingSurface().materialPtr() != r->planes[PLN_CEILING].surface.material) + if(s.ceilingSurface().materialPtr() != r->planes[PLN_CEILING].surface.material) df |= SDF_CEILING_MATERIAL; - if(r->lightLevel != s->lightLevel()) + if(r->lightLevel != s.lightLevel()) df |= SDF_LIGHT; - if(r->rgb[0] != s->lightColor().x) + if(r->rgb[0] != s.lightColor().x) df |= SDF_COLOR_RED; - if(r->rgb[1] != s->lightColor().y) + if(r->rgb[1] != s.lightColor().y) df |= SDF_COLOR_GREEN; - if(r->rgb[2] != s->lightColor().z) + if(r->rgb[2] != s.lightColor().z) df |= SDF_COLOR_BLUE; - if(r->planes[PLN_FLOOR].surface.rgba[0] != s->floorSurface().tintColor().x) + if(r->planes[PLN_FLOOR].surface.rgba[0] != s.floorSurface().tintColor().x) df |= SDF_FLOOR_COLOR_RED; - if(r->planes[PLN_FLOOR].surface.rgba[1] != s->floorSurface().tintColor().y) + if(r->planes[PLN_FLOOR].surface.rgba[1] != s.floorSurface().tintColor().y) df |= SDF_FLOOR_COLOR_GREEN; - if(r->planes[PLN_FLOOR].surface.rgba[2] != s->floorSurface().tintColor().z) + if(r->planes[PLN_FLOOR].surface.rgba[2] != s.floorSurface().tintColor().z) df |= SDF_FLOOR_COLOR_BLUE; - if(r->planes[PLN_CEILING].surface.rgba[0] != s->ceilingSurface().tintColor().x) + if(r->planes[PLN_CEILING].surface.rgba[0] != s.ceilingSurface().tintColor().x) df |= SDF_CEIL_COLOR_RED; - if(r->planes[PLN_CEILING].surface.rgba[1] != s->ceilingSurface().tintColor().y) + if(r->planes[PLN_CEILING].surface.rgba[1] != s.ceilingSurface().tintColor().y) df |= SDF_CEIL_COLOR_GREEN; - if(r->planes[PLN_CEILING].surface.rgba[2] != s->ceilingSurface().tintColor().z) + if(r->planes[PLN_CEILING].surface.rgba[2] != s.ceilingSurface().tintColor().z) df |= SDF_CEIL_COLOR_BLUE; // The cases where an immediate change to a plane's height is needed: @@ -668,46 +670,46 @@ dd_bool Sv_RegisterCompareSector(cregister_t *reg, int number, // The clientside height should be fixed. // Should we make an immediate change in floor height? - if(FEQUAL(r->planes[PLN_FLOOR].speed, 0) && FEQUAL(s->floor().speed(), 0)) + if(FEQUAL(r->planes[PLN_FLOOR].speed, 0) && FEQUAL(s.floor().speed(), 0)) { - if(!FEQUAL(r->planes[PLN_FLOOR].height, s->floor().height())) + if(!FEQUAL(r->planes[PLN_FLOOR].height, s.floor().height())) df |= SDF_FLOOR_HEIGHT; } else { - if(fabs(r->planes[PLN_FLOOR].height - s->floor().height()) > PLANE_SKIP_LIMIT) + if(fabs(r->planes[PLN_FLOOR].height - s.floor().height()) > PLANE_SKIP_LIMIT) df |= SDF_FLOOR_HEIGHT; } // How about the ceiling? - if(FEQUAL(r->planes[PLN_CEILING].speed, 0) && FEQUAL(s->ceiling().speed(), 0)) + if(FEQUAL(r->planes[PLN_CEILING].speed, 0) && FEQUAL(s.ceiling().speed(), 0)) { - if(!FEQUAL(r->planes[PLN_CEILING].height, s->ceiling().height())) + if(!FEQUAL(r->planes[PLN_CEILING].height, s.ceiling().height())) df |= SDF_CEILING_HEIGHT; } else { - if(fabs(r->planes[PLN_CEILING].height - s->ceiling().height()) > PLANE_SKIP_LIMIT) + if(fabs(r->planes[PLN_CEILING].height - s.ceiling().height()) > PLANE_SKIP_LIMIT) df |= SDF_CEILING_HEIGHT; } // Check planes, too. - if(!FEQUAL(r->planes[PLN_FLOOR].target, s->floor().targetHeight())) + if(!FEQUAL(r->planes[PLN_FLOOR].target, s.floor().targetHeight())) { // Target and speed are always sent together. df |= SDF_FLOOR_TARGET | SDF_FLOOR_SPEED; } - if(!FEQUAL(r->planes[PLN_FLOOR].speed, s->floor().speed())) + if(!FEQUAL(r->planes[PLN_FLOOR].speed, s.floor().speed())) { // Target and speed are always sent together. df |= SDF_FLOOR_SPEED | SDF_FLOOR_TARGET; } - if(!FEQUAL(r->planes[PLN_CEILING].target, s->ceiling().targetHeight())) + if(!FEQUAL(r->planes[PLN_CEILING].target, s.ceiling().targetHeight())) { // Target and speed are always sent together. df |= SDF_CEILING_TARGET | SDF_CEILING_SPEED; } - if(!FEQUAL(r->planes[PLN_CEILING].speed, s->ceiling().speed())) + if(!FEQUAL(r->planes[PLN_CEILING].speed, s.ceiling().speed())) { // Target and speed are always sent together. df |= SDF_CEILING_SPEED | SDF_CEILING_TARGET; @@ -717,7 +719,7 @@ dd_bool Sv_RegisterCompareSector(cregister_t *reg, int number, if(df & (SDF_CEILING_HEIGHT | SDF_CEILING_SPEED | SDF_CEILING_TARGET)) { LOGDEV_NET_XVERBOSE("Sector %i: ceiling state change noted (target = %f)") - << number << s->ceiling().targetHeight(); + << number << s.ceiling().targetHeight(); } #endif @@ -738,8 +740,8 @@ dd_bool Sv_RegisterCompareSector(cregister_t *reg, int number, { // The plane heights should be tracked regardless of the // change flags. - r->planes[PLN_FLOOR].height = s->floor().height(); - r->planes[PLN_CEILING].height = s->ceiling().height(); + r->planes[PLN_FLOOR].height = s.floor().height(); + r->planes[PLN_CEILING].height = s.ceiling().height(); } d->delta.flags = df; @@ -752,7 +754,7 @@ dd_bool Sv_RegisterCompareSector(cregister_t *reg, int number, dd_bool Sv_RegisterCompareSide(cregister_t *reg, uint number, sidedelta_t *d, byte doUpdate) { - LineSide const *side = App_WorldSystem().map().sideByIndex(number); + LineSide const *side = App_WorldSystem().map().sidePtr(number); dt_side_t *r = ®->sides[number]; byte lineFlags = side->line().flags() & 0xff; byte sideFlags = side->flags() & 0xff; @@ -1530,24 +1532,26 @@ coord_t Sv_MobjDistance(mobj_t const *mo, ownerinfo_t const *info, dd_bool isRea */ coord_t Sv_SectorDistance(int index, ownerinfo_t const *info) { - Sector const *sector = App_WorldSystem().map().sectors().at(index); + DENG2_ASSERT(info); + Sector const §or = App_WorldSystem().map().sector(index); - return M_ApproxDistance3(info->origin[VX] - sector->soundEmitter().origin[VX], - info->origin[VY] - sector->soundEmitter().origin[VY], - (info->origin[VZ] - sector->soundEmitter().origin[VZ]) * 1.2); + return M_ApproxDistance3(info->origin[0] - sector.soundEmitter().origin[0], + info->origin[1] - sector.soundEmitter().origin[1], + (info->origin[2] - sector.soundEmitter().origin[2]) * 1.2); } coord_t Sv_SideDistance(int index, int deltaFlags, ownerinfo_t const *info) { - LineSide const *side = App_WorldSystem().map().sideByIndex(index); + DENG2_ASSERT(info); + LineSide const *side = App_WorldSystem().map().sidePtr(index); - SoundEmitter const &emitter = (deltaFlags & SNDDF_SIDE_MIDDLE? side->middleSoundEmitter() - : deltaFlags & SNDDF_SIDE_TOP? side->topSoundEmitter() - : side->bottomSoundEmitter()); + SoundEmitter const &emitter = ( deltaFlags & SNDDF_SIDE_MIDDLE? side->middleSoundEmitter() + : deltaFlags & SNDDF_SIDE_TOP ? side->topSoundEmitter() + : side->bottomSoundEmitter()); - return M_ApproxDistance3(info->origin[VX] - emitter.origin[VX], - info->origin[VY] - emitter.origin[VY], - (info->origin[VZ] - emitter.origin[VZ]) * 1.2); + return M_ApproxDistance3(info->origin[0] - emitter.origin[0], + info->origin[1] - emitter.origin[1], + (info->origin[2] - emitter.origin[2]) * 1.2); } /** @@ -1581,7 +1585,7 @@ coord_t Sv_DeltaDistance(void const *deltaPtr, ownerinfo_t const *info) if(delta->type == DT_SIDE) { - LineSide *side = App_WorldSystem().map().sideByIndex(delta->id); + LineSide *side = App_WorldSystem().map().sidePtr(delta->id); Line &line = side->line(); return M_ApproxDistance(info->origin[VX] - line.center().x, info->origin[VY] - line.center().y); @@ -1589,9 +1593,9 @@ coord_t Sv_DeltaDistance(void const *deltaPtr, ownerinfo_t const *info) if(delta->type == DT_POLY) { - Polyobj *po = App_WorldSystem().map().polyobjs().at(delta->id); - return M_ApproxDistance(info->origin[VX] - po->origin[VX], - info->origin[VY] - po->origin[VY]); + Polyobj const &pob = App_WorldSystem().map().polyobj(delta->id); + return M_ApproxDistance(info->origin[VX] - pob.origin[VX], + info->origin[VY] - pob.origin[VY]); } if(delta->type == DT_MOBJ_SOUND) @@ -1612,9 +1616,9 @@ coord_t Sv_DeltaDistance(void const *deltaPtr, ownerinfo_t const *info) if(delta->type == DT_POLY_SOUND) { - Polyobj *po = App_WorldSystem().map().polyobjs().at(delta->id); - return M_ApproxDistance(info->origin[VX] - po->origin[VX], - info->origin[VY] - po->origin[VY]); + Polyobj const &pob = App_WorldSystem().map().polyobj(delta->id); + return M_ApproxDistance(info->origin[VX] - pob.origin[VX], + info->origin[VY] - pob.origin[VY]); } // Unknown distance. diff --git a/doomsday/server/src/serversystem.cpp b/doomsday/server/src/serversystem.cpp index a8c03c77b0..7c22af488f 100644 --- a/doomsday/server/src/serversystem.cpp +++ b/doomsday/server/src/serversystem.cpp @@ -1,6 +1,7 @@ /** @file serversystem.cpp Subsystem for tending to clients. * - * @authors Copyright (c) 2013 Jaakko Keränen + * @authors Copyright © 2013-2014 Jaakko Keränen + * @authors Copyright © 2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -17,33 +18,37 @@ */ #include "serversystem.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api_console.h" + #include "serverapp.h" #include "shellusers.h" #include "remoteuser.h" + #include "server/sv_def.h" #include "server/sv_frame.h" + #include "network/net_main.h" #include "network/net_buf.h" #include "network/net_event.h" #include "network/monitor.h" #include "network/masterserver.h" + #include "dd_main.h" #include "dd_loop.h" #include "sys_system.h" #include "world/map.h" #include "world/p_players.h" -#include "api_console.h" - -#include -#include -#include -#include -#include -#include -#include -#include - using namespace de; int nptIPPort = 0; ///< Server TCP port (cvar). @@ -55,32 +60,23 @@ static de::duint16 Server_ListenPort() DENG2_PIMPL(ServerSystem) { - bool inited; + bool inited = false; /// Beacon for informing clients that a server is present. - Beacon beacon; + Beacon beacon = { DEFAULT_UDP_PORT }; Time lastBeaconUpdateAt; - ListenSocket *serverSock; + ListenSocket *serverSock = nullptr; QMap users; ShellUsers shellUsers; - Instance(Public *i) - : Base(i), - inited(false), - beacon(DEFAULT_UDP_PORT), - serverSock(0) - {} - - ~Instance() - { - deinit(); - } + Instance(Public *i) : Base(i) {} + ~Instance() { deinit(); } bool isStarted() const { - return serverSock != 0; + return serverSock != nullptr; } bool init(duint16 port) @@ -92,6 +88,7 @@ DENG2_PIMPL(ServerSystem) deinit(); // Open a listening TCP socket. It will accept client connections. + DENG2_ASSERT(!serverSock); if(!(serverSock = new ListenSocket(port))) return false; @@ -100,7 +97,7 @@ DENG2_PIMPL(ServerSystem) // Update the beacon with the new port. beacon.start(port); - App_WorldSystem().audienceForMapChange += shellUsers; + App_WorldSystem().audienceForMapChange() += shellUsers; inited = true; return true; @@ -109,7 +106,7 @@ DENG2_PIMPL(ServerSystem) void clearUsers() { // Clear the client nodes. - foreach(RemoteUser *u, users.values()) + for(RemoteUser *u : users.values()) { delete u; } @@ -123,7 +120,7 @@ DENG2_PIMPL(ServerSystem) if(ServerApp::appExists()) { - App_WorldSystem().audienceForMapChange -= shellUsers; + App_WorldSystem().audienceForMapChange() -= shellUsers; } beacon.stop(); @@ -153,8 +150,8 @@ DENG2_PIMPL(ServerSystem) serverinfo_t info; Sv_GetInfo(&info); - QScopedPointer rec(Sv_InfoToRecord(&info)); - de::Block msg; + std::unique_ptr rec(Sv_InfoToRecord(&info)); + Block msg; de::Writer(msg).withHeader() << *rec; beacon.setMessage(msg); } @@ -179,8 +176,6 @@ DENG2_PIMPL(ServerSystem) void printStatus() { - int i, first; - if(serverSock) { LOG_NOTE("SERVER: Listening on TCP port %i") << serverSock->port(); @@ -190,8 +185,8 @@ DENG2_PIMPL(ServerSystem) LOG_NOTE("SERVER: No server socket open"); } - first = true; - for(i = 1; i < DDMAXPLAYERS; ++i) + int first = true; + for(int i = 1; i < DDMAXPLAYERS; ++i) { client_t *cl = &clients[i]; player_t *plr = &ddPlayers[i]; @@ -205,6 +200,7 @@ DENG2_PIMPL(ServerSystem) LOG_MSG(_E(m) "P# Name: Nd Jo Hs Rd Gm Age:"); first = false; } + LOG_MSG(_E(m) "%2i %-10s %2i %c %c %c %c %f sec") << i << cl->name << cl->nodeID << (user->isJoined()? '*' : ' ') @@ -269,14 +265,12 @@ RemoteUser &ServerSystem::user(Id const &id) const bool ServerSystem::isUserAllowedToJoin(RemoteUser &/*user*/) const { // If the server is full, attempts to connect are canceled. - if(Sv_GetNumConnected() >= svMaxPlayers) - return false; - - return true; + return (Sv_GetNumConnected() < svMaxPlayers); } void ServerSystem::convertToShellUser(RemoteUser *user) { + DENG2_ASSERT(user); LOG_AS("convertToShellUser"); Socket *socket = user->takeSocket(); @@ -295,9 +289,11 @@ void ServerSystem::timeChanged(Clock const &clock) Garbage_Recycle(); // Adjust loop rate depending on whether players are in game. - int i, count = 0; - for(i = 1; i < DDMAXPLAYERS; ++i) + int count = 0; + for(int i = 1; i < DDMAXPLAYERS; ++i) + { if(ddPlayers[i].shared.inGame) count++; + } DENG2_TEXT_APP->loop().setRate(count? 35 : 3); @@ -357,7 +353,7 @@ ServerSystem &App_ServerSystem() //--------------------------------------------------------------------------- -void Server_Register(void) +void Server_Register() { C_VAR_INT("net-ip-port", &nptIPPort, CVF_NO_MAX, 0, 0); @@ -366,21 +362,23 @@ void Server_Register(void) #endif } -dd_bool N_ServerOpen(void) +dd_bool N_ServerOpen() { App_ServerSystem().start(Server_ListenPort()); - // The game module may have something that needs doing before we - // actually begin. + // The game module may have something that needs doing before we actually begin. if(gx.NetServerStart) + { gx.NetServerStart(true); + } Sv_StartNetGame(); - // The game DLL might want to do something now that the - // server is started. + // The game DLL might want to do something now that the server is started. if(gx.NetServerStart) + { gx.NetServerStart(false); + } if(masterAware) { @@ -391,7 +389,7 @@ dd_bool N_ServerOpen(void) return true; } -dd_bool N_ServerClose(void) +dd_bool N_ServerClose() { if(!App_ServerSystem().isListening()) return true; @@ -403,19 +401,23 @@ dd_bool N_ServerClose(void) } if(gx.NetServerStop) + { gx.NetServerStop(true); + } Net_StopGame(); Sv_StopNetGame(); if(gx.NetServerStop) + { gx.NetServerStop(false); + } App_ServerSystem().stop(); return true; } -void N_PrintNetworkStatus(void) +void N_PrintNetworkStatus() { App_ServerSystem().printStatus(); } diff --git a/doomsday/server/src/shelluser.cpp b/doomsday/server/src/shelluser.cpp index 16428a58f0..c405939b44 100644 --- a/doomsday/server/src/shelluser.cpp +++ b/doomsday/server/src/shelluser.cpp @@ -1,7 +1,7 @@ /** @file shelluser.cpp Remote user of a shell connection. * * @authors Copyright © 2013 Jaakko Keränen - * @authors Copyright © 2013 Daniel Swanson + * @authors Copyright © 2013-2014 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -18,16 +18,18 @@ */ #include "shelluser.h" + #include #include -#include #include #include - -#include "de_base.h" +#include #include #include + #include "api_console.h" + +#include "dd_main.h" #include "games.h" #include "Game" #include "network/net_main.h" @@ -138,15 +140,16 @@ void ShellUser::sendMapOutline() { if(!App_WorldSystem().hasMap()) return; - QScopedPointer packet(new shell::MapOutlinePacket); + std::unique_ptr packet(new shell::MapOutlinePacket); - foreach(Line *line, App_WorldSystem().map().lines()) + App_WorldSystem().map().forAllLines([&packet] (Line &line) { - packet->addLine(Vector2i(line->fromOrigin().x, line->fromOrigin().y), - Vector2i(line->toOrigin().x, line->toOrigin().y), - (line->hasFrontSector() && line->hasBackSector())? + packet->addLine(Vector2i(line.fromOrigin().x, line.fromOrigin().y), + Vector2i(line.toOrigin().x, line.toOrigin().y), + (line.hasFrontSector() && line.hasBackSector())? shell::MapOutlinePacket::TwoSidedLine : shell::MapOutlinePacket::OneSidedLine); - } + return LoopContinue; + }); *this << *packet; }