diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index 010894c97d..18b1e6552a 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -116,7 +116,6 @@ DENG_CONVENIENCE_HEADERS += \ include/BiasDigest \ include/BiasIllum \ include/BiasSource \ - include/BiasSurface \ include/BiasTracker \ include/BitmapFont \ include/BspLeaf \ @@ -281,7 +280,6 @@ DENG_HEADERS += \ include/render/biasdigest.h \ include/render/biasillum.h \ include/render/biassource.h \ - include/render/biassurface.h \ include/render/biastracker.h \ include/render/billboard.h \ include/render/blockmapvisual.h \ @@ -617,7 +615,6 @@ SOURCES += \ src/render/biasdigest.cpp \ src/render/biasillum.cpp \ src/render/biassource.cpp \ - src/render/biassurface.cpp \ src/render/biastracker.cpp \ src/render/billboard.cpp \ src/render/blockmapvisual.cpp \ diff --git a/doomsday/client/include/BiasSurface b/doomsday/client/include/BiasSurface deleted file mode 100644 index 2ce4cb50f0..0000000000 --- a/doomsday/client/include/BiasSurface +++ /dev/null @@ -1 +0,0 @@ -#include "render/biassurface.h" diff --git a/doomsday/client/include/render/biassurface.h b/doomsday/client/include/render/biassurface.h deleted file mode 100644 index a9d6cbdb27..0000000000 --- a/doomsday/client/include/render/biassurface.h +++ /dev/null @@ -1,62 +0,0 @@ -/** @file biassurface.h Shadow Bias surface. - * - * @authors Copyright © 2013 Daniel Swanson - * - * @par License - * GPL: http://www.gnu.org/licenses/gpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. You should have received a copy of the GNU - * General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#ifndef DENG_RENDER_SHADOWBIAS_SURFACE_H -#define DENG_RENDER_SHADOWBIAS_SURFACE_H - -#include - -/** - * Base class for a surface which supports lighting within the Shadow Bias - * lighting model. - */ -class BiasSurface -{ - -public: - virtual ~BiasSurface() {} - - /** - * To be called to register the commands and variables of this module. - */ - static void consoleRegister(); - - /** - * Perform lighting for the supplied geometry. The derived class wil define - * how these vertices map to bias illumination points. - * - * @param group Geometry group identifier. - * @param posCoords World coordinates for each vertex. - * @param colorCoords Final lighting values will be written here. - */ - virtual void lightBiasPoly(int group, de::Vector3f const *posCoords, - de::Vector4f *colorCoords) = 0; - - /** - * Schedule a lighting update to a geometry group following a move of some - * other element of dependent geometry. Derived classes may override this - * with their own update logic. The default implementation does nothing. - * - * @param group Geometry group identifier. - */ - virtual void updateBiasAfterGeometryMove(int group) {} -}; - -extern int devUpdateBiasContributors; //cvar - -#endif // DENG_RENDER_SHADOWBIAS_SURFACE_H diff --git a/doomsday/client/include/world/bspleaf.h b/doomsday/client/include/world/bspleaf.h index c1f25188a0..0b0567055d 100644 --- a/doomsday/client/include/world/bspleaf.h +++ b/doomsday/client/include/world/bspleaf.h @@ -27,10 +27,6 @@ #include "Mesh" -#ifdef __CLIENT__ -# include "BiasSurface" -#endif - #include #include #include @@ -38,7 +34,6 @@ class SectorCluster; struct polyobj_s; #ifdef __CLIENT__ -class BiasDigest; class Lumobj; #endif @@ -60,9 +55,6 @@ class Lumobj; * @ingroup world */ class BspLeaf : public de::MapElement -#ifdef __CLIENT__ -, public BiasSurface -#endif { DENG2_NO_COPY (BspLeaf) DENG2_NO_ASSIGN(BspLeaf) @@ -273,28 +265,6 @@ class BspLeaf : public de::MapElement */ int numFanVertices() const; - /** - * Perform bias lighting for the supplied geometry. - * - * @important It is assumed there are least @ref numFanVertices() elements! - * - * @param group Geometry group identifier. - * @param posCoords World coordinates for each vertex. - * @param colorCoords Final lighting values will be written here. - */ - void lightBiasPoly(int group, de::Vector3f const *posCoords, - de::Vector4f *colorCoords); - - void updateBiasAfterGeometryMove(int group); - - /** - * Apply bias lighting changes to @em all map element geometries at this - * leaf of the BSP. - * - * @param changes Digest of lighting changes to be applied. - */ - void applyBiasDigest(BiasDigest &changes); - /** * Recalculate the environmental audio characteristics (reverb) of the BSP leaf. */ diff --git a/doomsday/client/include/world/line.h b/doomsday/client/include/world/line.h index 9ff920bb96..f0e5ead5da 100644 --- a/doomsday/client/include/world/line.h +++ b/doomsday/client/include/world/line.h @@ -27,10 +27,6 @@ #include "Polyobj" #include "Vertex" -#ifdef __CLIENT__ -# include "BiasSurface" -#endif - #include #include #include @@ -121,9 +117,6 @@ class Line : public de::MapElement * Side geometry segment on the XY plane. */ class Segment : public de::MapElement -#ifdef __CLIENT__ - , public BiasSurface -#endif { DENG2_NO_COPY (Segment) DENG2_NO_ASSIGN(Segment) @@ -194,28 +187,6 @@ class Line : public de::MapElement */ void setFrontFacing(bool yes = true); - /** - * Perform bias lighting for the supplied geometry. - * - * @important It is assumed there are least @em four elements! - * - * @param group Geometry group identifier. - * @param posCoords World coordinates for each vertex. - * @param colorCoords Final lighting values will be written here. - */ - void lightBiasPoly(int group, de::Vector3f const *posCoords, - de::Vector4f *colorCoords); - - void updateBiasAfterGeometryMove(int group); - - /** - * Apply bias lighting changes to @em all map element geometries - * for the segment. - * - * @param changes Digest of lighting changes to be applied. - */ - void applyBiasDigest(BiasDigest &changes); - #endif // __CLIENT__ private: diff --git a/doomsday/client/include/world/sectorcluster.h b/doomsday/client/include/world/sectorcluster.h index f781a72229..f5df1b4b51 100644 --- a/doomsday/client/include/world/sectorcluster.h +++ b/doomsday/client/include/world/sectorcluster.h @@ -233,13 +233,6 @@ class SectorCluster */ AudioEnvironmentFactors const &reverb() const; - /** - * Apply bias lighting changes to @em all surfaces within the cluster. - * - * @param changes Digest of lighting changes to be applied. - */ - void applyBiasDigest(BiasDigest &changes); - /** * Returns the unique identifier of the light source. */ @@ -271,8 +264,40 @@ class SectorCluster */ int blockLightSourceZBias(); + /** + * Apply bias lighting changes to @em all geometry Shards within the cluster. + * + * @param changes Digest of lighting changes to be applied. + */ + void applyBiasDigest(BiasDigest &changes); + + /** + * Perform bias lighting for the supplied Shard geometry. + * + * @param mapElement MapElement for the geometry to be lit. + * @param geomId MapElement-unique geometry id. + * @param posCoords World coordinates for each vertex. + * @param colorCoords Final lighting values will be written here. + */ + void applyBiasLightSources(de::MapElement &mapElement, int geomId, + de::Vector3f const *posCoords, de::Vector4f *colorCoords); + + /** + * Schedule a lighting update to a geometry Shard following a move of some + * other element of dependent geometry. + * + * @param mapElement MapElement for the geometry to be updated. + * @param geomId MapElement-unique geometry id. + */ + void updateBiasAfterGeometryMove(de::MapElement &mapElement, int geomId); + #endif // __CLIENT__ + /** + * To be called to register the commands and variables of this module. + */ + static void consoleRegister(); + private: DENG2_PRIVATE(d) }; diff --git a/doomsday/client/src/render/biassurface.cpp b/doomsday/client/src/render/biassurface.cpp deleted file mode 100644 index f59a498655..0000000000 --- a/doomsday/client/src/render/biassurface.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/** @file biassurface.cpp Shadow Bias surface. - * - * @authors Copyright © 2013 Daniel Swanson - * - * @par License - * GPL: http://www.gnu.org/licenses/gpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. You should have received a copy of the GNU - * General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include "de_console.h" - -#include "render/biassurface.h" - -int devUpdateBiasContributors = true; //cvar - -void BiasSurface::consoleRegister() // static -{ - // Development variables. - C_VAR_INT("rend-dev-bias-affected", &devUpdateBiasContributors, CVF_NO_ARCHIVE, 0, 1); -} diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index aca6e556d5..9ca8c57a3d 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -62,7 +62,6 @@ #include "Surface" #include "BiasIllum" -#include "BiasSurface" #include "HueCircleVisual" #include "LightDecoration" #include "Lumobj" @@ -423,7 +422,6 @@ void Rend_Register() C_CMD_FLAGS ("texreset", "s", TexReset, CMDF_NO_DEDICATED); BiasIllum::consoleRegister(); - BiasSurface::consoleRegister(); LightDecoration::consoleRegister(); LightGrid::consoleRegister(); Lumobj::consoleRegister(); @@ -1001,7 +999,7 @@ struct rendworldpoly_params_t uint shadowListIdx; // List of shadows that affect this poly. float glowing; bool forceOpaque; - BiasSurface *bsuf; + MapElement *mapElement; int geomGroup; bool isWall; @@ -1169,7 +1167,7 @@ static bool renderWorldPoly(Vector3f *posCoords, uint numVertices, } // Apply bias light source contributions. - p.bsuf->lightBiasPoly(p.geomGroup, posCoords, colorCoords); + leaf->cluster().applyBiasLightSources(*p.mapElement, p.geomGroup, posCoords, colorCoords); // Apply surface glow. if(p.glowing > 0) @@ -1861,7 +1859,7 @@ static void writeWallSection(HEdge &hedge, int section, Vector3d bottomRight = rightEdge.bottom().origin(); parm.skyMasked = skyMasked; - parm.bsuf = &segment; + parm.mapElement = &segment; parm.geomGroup = wallSpec.section; parm.topLeft = &topLeft; parm.bottomRight = &bottomRight; @@ -2088,7 +2086,7 @@ static void writeLeafPlane(Plane &plane) rendworldpoly_params_t parm; zap(parm); - parm.bsuf = leaf; + parm.mapElement = leaf; parm.geomGroup = plane.indexInSector(); parm.topLeft = &topLeft; parm.bottomRight = &bottomRight; diff --git a/doomsday/client/src/world/bspleaf.cpp b/doomsday/client/src/world/bspleaf.cpp index 5acb8c9740..152320a1ff 100644 --- a/doomsday/client/src/world/bspleaf.cpp +++ b/doomsday/client/src/world/bspleaf.cpp @@ -31,13 +31,6 @@ #include "SectorCluster" #include "Surface" -#ifdef __CLIENT__ -# include "BiasDigest" -# include "BiasIllum" -# include "BiasSource" -# include "BiasTracker" -#endif - #include #include #include @@ -52,20 +45,6 @@ ddouble triangleArea(Vector2d const &v1, Vector2d const &v2, Vector2d const &v3) return (a.x * b.y - b.x * a.y) / 2; } -#ifdef __CLIENT__ -struct GeometryGroup -{ - typedef QList BiasIllums; - - uint biasLastUpdateFrame; - BiasIllums biasIllums; - BiasTracker biasTracker; -}; - -/// Geometry group identifier => group data. -typedef QMap GeometryGroups; -#endif - DENG2_PIMPL(BspLeaf) { SectorCluster *cluster; ///< Attributed cluster (if any, not owned). @@ -84,7 +63,6 @@ DENG2_PIMPL(BspLeaf) bool needUpdateFanBase; ///< @c true= need to rechoose a fan base half-edge. int addSpriteCount; ///< Frame number of last R_AddSprites. - GeometryGroups geomGroups; Lumobjs lumobjs; ///< Linked lumobjs (not owned). ShadowLines shadowLines; ///< Linked map lines for fake radio shadowing. AudioEnvironmentFactors reverb; ///< Cached characteristics. @@ -190,89 +168,6 @@ DENG2_PIMPL(BspLeaf) #undef MIN_TRIANGLE_EPSILON } - - /** - * Retrieve geometry data by it's associated unique @a group identifier. - * - * @param group Geometry group identifier. - * @param canAlloc @c true= to allocate if no data exists. Note that the - * number of vertices in the fan geometry must be known - * at this time. - */ - GeometryGroup *geometryGroup(int group, bool canAlloc = true) - { - DENG2_ASSERT(cluster != 0); // sanity check - DENG2_ASSERT(group >= 0 && group < cluster->sector().planeCount()); // sanity check - - GeometryGroups::iterator foundAt = geomGroups.find(group); - if(foundAt != geomGroups.end()) - { - return &*foundAt; - } - - if(!canAlloc) return 0; - - // Number of bias illumination points for this geometry. Presently we - // define a 1:1 mapping to fan geometry vertices. - int numBiasIllums = self.numFanVertices(); - - GeometryGroup &newGeomGroup = *geomGroups.insert(group, GeometryGroup()); - newGeomGroup.biasIllums.reserve(numBiasIllums); - for(int i = 0; i < numBiasIllums; ++i) - { - newGeomGroup.biasIllums.append(BiasIllum(&newGeomGroup.biasTracker)); - } - - return &newGeomGroup; - } - - /** - * @todo This could be enhanced so that only the lights on the right - * side of the surface are taken into consideration. - */ - void updateBiasContributors(GeometryGroup &geomGroup, int planeIndex) - { - // If the data is already up to date, nothing needs to be done. - uint lastChangeFrame = self.map().biasLastChangeOnFrame(); - if(geomGroup.biasLastUpdateFrame == lastChangeFrame) - return; - - geomGroup.biasLastUpdateFrame = lastChangeFrame; - - geomGroup.biasTracker.clearContributors(); - - Plane const &plane = cluster->visPlane(planeIndex); - Surface const &surface = plane.surface(); - - Vector3d surfacePoint(poly->center(), plane.heightSmoothed()); - - foreach(BiasSource *source, self.map().biasSources()) - { - // If the source is too weak we will ignore it completely. - if(source->intensity() <= 0) - continue; - - Vector3d sourceToSurface = (source->origin() - surfacePoint).normalize(); - coord_t distance = 0; - - // Calculate minimum 2D distance to the BSP leaf. - /// @todo This is probably too accurate an estimate. - HEdge *baseNode = poly->hedge(); - HEdge *node = baseNode; - do - { - 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; - - geomGroup.biasTracker.addContributor(source, source->evaluateIntensity() / de::max(distance, 1.0)); - } - } - #endif // __CLIENT__ }; @@ -440,78 +335,6 @@ int BspLeaf::numFanVertices() const return d->poly->hedgeCount() + (fanBase()? 0 : 2); } -static void updateBiasForWallSectionsAfterGeometryMove(HEdge *hedge) -{ - if(!hedge || !hedge->hasMapElement()) - return; - - LineSideSegment &seg = hedge->mapElementAs(); - seg.updateBiasAfterGeometryMove(LineSide::Middle); - seg.updateBiasAfterGeometryMove(LineSide::Bottom); - seg.updateBiasAfterGeometryMove(LineSide::Top); -} - -void BspLeaf::updateBiasAfterGeometryMove(int group) -{ - if(!hasPoly()) return; - - if(GeometryGroup *geomGroup = d->geometryGroup(group, false /*don't allocate*/)) - { - geomGroup->biasTracker.updateAllContributors(); - } - - HEdge *base = poly().hedge(); - HEdge *hedge = base; - do - { - updateBiasForWallSectionsAfterGeometryMove(hedge); - } while((hedge = &hedge->next()) != base); - - foreach(Mesh *mesh, extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) - { - updateBiasForWallSectionsAfterGeometryMove(hedge); - } -} - -void BspLeaf::applyBiasDigest(BiasDigest &changes) -{ - if(!hasPoly()) return; - - for(GeometryGroups::iterator it = d->geomGroups.begin(); - it != d->geomGroups.end(); ++it) - { - it.value().biasTracker.applyChanges(changes); - } -} - -void BspLeaf::lightBiasPoly(int group, Vector3f const *posCoords, Vector4f *colorCoords) -{ - DENG2_ASSERT(posCoords != 0 && colorCoords != 0); - - int const planeIndex = group; - GeometryGroup *geomGroup = d->geometryGroup(planeIndex); - - // Should we update? - if(devUpdateBiasContributors) - { - d->updateBiasContributors(*geomGroup, planeIndex); - } - - Surface const &surface = cluster().visPlane(planeIndex).surface(); - uint const biasTime = map().biasCurrentTime(); - - Vector3f const *posIt = posCoords; - Vector4f *colorIt = colorCoords; - for(int i = 0; i < geomGroup->biasIllums.count(); ++i, posIt++, colorIt++) - { - *colorIt += geomGroup->biasIllums[i].evaluate(*posIt, surface.normal(), biasTime); - } - - // Any changes from contributors will have now been applied. - geomGroup->biasTracker.markIllumUpdateCompleted(); -} - static void accumReverbForWallSections(HEdge const *hedge, float envSpaceAccum[NUM_AUDIO_ENVIRONMENTS], float &total) { diff --git a/doomsday/client/src/world/line.cpp b/doomsday/client/src/world/line.cpp index 7ba95157d3..cbd89f34eb 100644 --- a/doomsday/client/src/world/line.cpp +++ b/doomsday/client/src/world/line.cpp @@ -36,11 +36,6 @@ #ifdef __CLIENT__ # include "world/map.h" - -# include "BiasDigest" -# include "BiasIllum" -# include "BiasSource" -# include "BiasTracker" #endif #include #include @@ -52,20 +47,6 @@ using namespace de; -#ifdef __CLIENT__ -struct GeometryGroup -{ - typedef QList BiasIllums; - - uint biasLastUpdateFrame; - BiasIllums biasIllums; - BiasTracker biasTracker; -}; - -/// Geometry group identifier => group data. -typedef QMap GeometryGroups; -#endif - DENG2_PIMPL_NOREF(Line::Side::Segment) { /// Half-edge attributed to the line segment (not owned). @@ -78,8 +59,6 @@ DENG2_PIMPL_NOREF(Line::Side::Segment) /// Accurate length of the segment. coord_t length; - /// Bias lighting data for each geometry group (i.e., each Line::Side section). - GeometryGroups geomGroups; bool frontFacing; #endif @@ -91,85 +70,6 @@ DENG2_PIMPL_NOREF(Line::Side::Segment) , frontFacing(false) #endif {} - -#ifdef __CLIENT__ - /** - * Retrieve geometry data by it's associated unique @a group identifier. - * - * @param group Geometry group identifier. - * @param canAlloc @c true= to allocate if no data exists. - */ - GeometryGroup *geometryGroup(int group, bool canAlloc = true) - { - DENG2_ASSERT(group >= 0 && group < 3); // sanity check - - GeometryGroups::iterator foundAt = geomGroups.find(group); - if(foundAt != geomGroups.end()) - { - return &*foundAt; - } - - if(!canAlloc) return 0; - - // Number of bias illumination points for this geometry. Presently we - // define a 1:1 mapping to strip geometry vertices. - int const numBiasIllums = 4; - - GeometryGroup &newGeomGroup = *geomGroups.insert(group, GeometryGroup()); - newGeomGroup.biasIllums.reserve(numBiasIllums); - for(int i = 0; i < numBiasIllums; ++i) - { - newGeomGroup.biasIllums.append(BiasIllum(&newGeomGroup.biasTracker)); - } - - return &newGeomGroup; - } - - /** - * @todo This could be enhanced so that only the lights on the right - * side of the surface are taken into consideration. - */ - void updateBiasContributors(Line::Side const &lineSide, - GeometryGroup &geomGroup, int /*sectionIndex*/) - { - // If the data is already up to date, nothing needs to be done. - uint lastChangeFrame = lineSide.map().biasLastChangeOnFrame(); - if(geomGroup.biasLastUpdateFrame == lastChangeFrame) - return; - - geomGroup.biasLastUpdateFrame = lastChangeFrame; - - geomGroup.biasTracker.clearContributors(); - - Surface const &surface = lineSide.middle(); - Vector2d const &from = hedge->origin(); - Vector2d const &to = hedge->twin().origin(); - Vector2d const center = (from + to) / 2; - - foreach(BiasSource *source, lineSide.map().biasSources()) - { - // If the source is too weak we will ignore it completely. - if(source->intensity() <= 0) - continue; - - 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(); - if(k == 0 || len < distance) - distance = len; - } - - if(sourceToSurface.dot(surface.normal()) < 0) - continue; - - geomGroup.biasTracker.addContributor(source, source->evaluateIntensity() / de::max(distance, 1.0)); - } - } -#endif }; Line::Side::Segment::Segment(Line::Side &lineSide, HEdge &hedge) @@ -215,52 +115,6 @@ void Line::Side::Segment::setFrontFacing(bool yes) d->frontFacing = yes; } -void Line::Side::Segment::updateBiasAfterGeometryMove(int group) -{ - if(GeometryGroup *geomGroup = d->geometryGroup(group, false /*don't allocate*/)) - { - geomGroup->biasTracker.updateAllContributors(); - } -} - -void Line::Side::Segment::applyBiasDigest(BiasDigest &changes) -{ - for(GeometryGroups::iterator it = d->geomGroups.begin(); - it != d->geomGroups.end(); ++it) - { - it.value().biasTracker.applyChanges(changes); - } -} - -void Line::Side::Segment::lightBiasPoly(int group, Vector3f const *posCoords, - Vector4f *colorCoords) -{ - DENG_ASSERT(posCoords != 0 && colorCoords != 0); - - Line::Side const &side = lineSide(); - int const sectionIndex = group; - GeometryGroup *geomGroup = d->geometryGroup(sectionIndex); - - // Should we update? - if(devUpdateBiasContributors) - { - d->updateBiasContributors(side, *geomGroup, sectionIndex); - } - - Surface const &surface = side.surface(sectionIndex); - uint const biasTime = map().biasCurrentTime(); - - Vector3f const *posIt = posCoords; - Vector4f *colorIt = colorCoords; - for(int i = 0; i < geomGroup->biasIllums.count(); ++i, posIt++, colorIt++) - { - *colorIt += geomGroup->biasIllums[i].evaluate(*posIt, surface.normal(), biasTime); - } - - // Any changes from contributors will have now been applied. - geomGroup->biasTracker.markIllumUpdateCompleted(); -} - #endif // __CLIENT__ /** diff --git a/doomsday/client/src/world/map.cpp b/doomsday/client/src/world/map.cpp index 70cacf54a3..523e03e08c 100644 --- a/doomsday/client/src/world/map.cpp +++ b/doomsday/client/src/world/map.cpp @@ -3665,6 +3665,8 @@ D_CMD(InspectMap) void Map::consoleRegister() // static { Mobj_ConsoleRegister(); + SectorCluster::consoleRegister(); + C_VAR_INT("bsp-factor", &bspSplitFactor, CVF_NO_MAX, 0, 0); #ifdef __CLIENT__ C_VAR_INT("rend-bias-grid-multisample", &lgMXSample, 0, 0, 7); diff --git a/doomsday/client/src/world/polyobj.cpp b/doomsday/client/src/world/polyobj.cpp index 4559954c61..066cf3169f 100644 --- a/doomsday/client/src/world/polyobj.cpp +++ b/doomsday/client/src/world/polyobj.cpp @@ -25,6 +25,9 @@ #include "world/map.h" #include "world/p_object.h" #include "BspLeaf" +#ifdef __CLIENT__ +# include "SectorCluster" +#endif #ifdef __CLIENT__ # include "render/rend_main.h" // useBias @@ -54,8 +57,9 @@ static void notifyGeometryChanged(Polyobj &po) if(!hedge->hasMapElement()) continue; - hedge->mapElementAs(). - updateBiasAfterGeometryMove(LineSide::Middle); + /// @note If polyobjs are allowed to move between sector clusters + /// then we'll need to revise the bias illumination storage specially. + po.bspLeaf().cluster().updateBiasAfterGeometryMove(hedge->mapElement(), LineSide::Middle); } } #else // !__CLIENT__ diff --git a/doomsday/client/src/world/sectorcluster.cpp b/doomsday/client/src/world/sectorcluster.cpp index 8d0cfe1c55..f24115a05d 100644 --- a/doomsday/client/src/world/sectorcluster.cpp +++ b/doomsday/client/src/world/sectorcluster.cpp @@ -21,6 +21,7 @@ #include "de_platform.h" #include "world/sectorcluster.h" +#include "con_main.h" #include "Face" #include "BspLeaf" @@ -33,6 +34,8 @@ #ifdef __CLIENT__ # include "render/rend_main.h" // useBias +# include "BiasIllum" +# include "BiasTracker" #endif #include @@ -44,8 +47,8 @@ namespace de { -namespace internal { - +namespace internal +{ /// Classification flags: enum ClusterFlag { @@ -58,16 +61,35 @@ namespace internal { Q_DECLARE_FLAGS(ClusterFlags, ClusterFlag) Q_DECLARE_OPERATORS_FOR_FLAGS(ClusterFlags) + + static QRectF qrectFromAABox(AABoxd const &aaBox) + { + return QRectF(QPointF(aaBox.minX, aaBox.maxY), QPointF(aaBox.maxX, aaBox.minY)); + } } } using namespace de; using namespace de::internal; -static QRectF qrectFromAABox(AABoxd const &aaBox) +#ifdef __CLIENT__ +static int devUpdateBiasContributors = true; //cvar +#endif + +#ifdef __CLIENT__ +/** + * A fragment of 3D map geometry. + */ +struct Shard { - return QRectF(QPointF(aaBox.minX, aaBox.maxY), QPointF(aaBox.maxX, aaBox.minY)); -} + struct BiasData { + uint lastUpdateFrame; + typedef QList BiasIllums; + BiasIllums illums; + BiasTracker tracker; + } bias; +}; +#endif DENG2_PIMPL(SectorCluster) , DENG2_OBSERVES(SectorCluster, Deletion) @@ -97,6 +119,11 @@ DENG2_PIMPL(SectorCluster) QScopedPointer boundaryInfo; #ifdef __CLIENT__ + /// @todo Avoid two-stage lookup. + typedef QMap Shards; + typedef QMap GeometryGroups; + GeometryGroups geomGroups; + /// BSP leafs in the neighborhood effecting environmental audio characteristics. typedef QSet ReverbBspLeafs; ReverbBspLeafs reverbBspLeafs; @@ -629,6 +656,19 @@ DENG2_PIMPL(SectorCluster) clearMapping(plane.indexInSector()); } +#ifdef __CLIENT__ + void updateBiasForWallSectionsAfterGeometryMove(HEdge *hedge) + { + if(!hedge) return; + if(!hedge->hasMapElement()) return; + + MapElement *mapElement = &hedge->mapElement(); + self.updateBiasAfterGeometryMove(*mapElement, LineSide::Middle); + self.updateBiasAfterGeometryMove(*mapElement, LineSide::Bottom); + self.updateBiasAfterGeometryMove(*mapElement, LineSide::Top); + } +#endif + /// Observes Plane HeightChange. void planeHeightChanged(Plane &plane) { @@ -661,7 +701,20 @@ DENG2_PIMPL(SectorCluster) // Inform bias surfaces of changed geometry. foreach(BspLeaf *bspLeaf, bspLeafs) { - bspLeaf->updateBiasAfterGeometryMove(plane.indexInSector()); + self.updateBiasAfterGeometryMove(*bspLeaf, plane.indexInSector()); + + HEdge *base = bspLeaf->poly().hedge(); + HEdge *hedge = base; + do + { + updateBiasForWallSectionsAfterGeometryMove(hedge); + } while((hedge = &hedge->next()) != base); + + foreach(Mesh *mesh, bspLeaf->extraMeshes()) + foreach(HEdge *hedge, mesh->hedges()) + { + updateBiasForWallSectionsAfterGeometryMove(hedge); + } } } @@ -674,15 +727,175 @@ DENG2_PIMPL(SectorCluster) #ifdef __CLIENT__ - /// Observes Plane HeightSmoothedChange. - void planeHeightSmoothedChanged(Plane &plane) + // Determine the number of bias illumination points needed for this geometry. + // Presently we define a 1:1 mapping geometry vertices. + static int countIlluminationPoints(MapElement &mapElement, int group) { - markDependantSurfacesForDecorationUpdate(); + switch(mapElement.type()) + { + case DMU_BSPLEAF: { + BspLeaf &bspLeaf = mapElement.as(); + DENG2_ASSERT(group >= 0 && group < bspLeaf.sector().planeCount()); // sanity check + return bspLeaf.numFanVertices(); } - // We may need to update one or both mapped planes. - maybeInvalidateMapping(plane.indexInSector()); + case DMU_SEGMENT: + DENG2_ASSERT(group >= 0 && group <= LineSide::Top); // sanity check + return 4; + + default: DENG2_ASSERT(!"SectorCluster::countIlluminationPoints: Unsupported MapElement type"); + } + return 0; + } + + /** + * Find the geometry Shard for a MapElement by the element-unique @a group + * identifier. + * + * @param geomId Geometry identifier. + * @param canAlloc @c true= to allocate if no data exists. Note that the + * number of vertices in the fan geometry must be known + * at this time. + */ + Shard *shard(MapElement &mapElement, int geomId, bool canAlloc = true) + { + GeometryGroups::iterator foundGroup = geomGroups.find(&mapElement); + if(foundGroup != geomGroups.end()) + { + Shards &shards = *foundGroup; + Shards::iterator found = shards.find(geomId); + if(found != shards.end()) + { + return &*found; + } + } + + if(!canAlloc) return 0; + + if(foundGroup == geomGroups.end()) + { + foundGroup = geomGroups.insert(&mapElement, Shards()); + } + + Shard &newShard = *foundGroup->insert(geomId, Shard()); + + int const neededIllumCount = countIlluminationPoints(mapElement, geomId); + newShard.bias.illums.reserve(neededIllumCount); + for(int i = 0; i < neededIllumCount; ++i) + { + newShard.bias.illums.append(BiasIllum(&newShard.bias.tracker)); + } + + return &newShard; + } + + /** + * @todo This could be enhanced so that only the lights on the right + * side of the surface are taken into consideration. + */ + void updateBiasContributors(Shard &shard, BspLeaf &bspLeaf, int geomId) + { + Map &map = bspLeaf.map(); + + // If the data is already up to date, nothing needs to be done. + uint lastChangeFrame = map.biasLastChangeOnFrame(); + if(shard.bias.lastUpdateFrame == lastChangeFrame) + return; + + shard.bias.lastUpdateFrame = lastChangeFrame; + + shard.bias.tracker.clearContributors(); + + Plane const &plane = self.visPlane(geomId); + Surface const &surface = plane.surface(); + + Vector3d surfacePoint(bspLeaf.poly().center(), plane.heightSmoothed()); + + foreach(BiasSource *source, map.biasSources()) + { + // If the source is too weak we will ignore it completely. + if(source->intensity() <= 0) + continue; + + Vector3d sourceToSurface = (source->origin() - surfacePoint).normalize(); + coord_t distance = 0; + + // Calculate minimum 2D distance to the BSP leaf. + /// @todo This is probably too accurate an estimate. + HEdge *baseNode = bspLeaf.poly().hedge(); + HEdge *node = baseNode; + do + { + 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; + + shard.bias.tracker.addContributor(source, source->evaluateIntensity() / de::max(distance, 1.0)); + } } + /** + * @todo This could be enhanced so that only the lights on the right + * side of the surface are taken into consideration. + */ + void updateBiasContributors(Shard &shard, LineSideSegment const &seg, int /*geomId*/) + { + Map &map = seg.map(); + + // If the data is already up to date, nothing needs to be done. + uint lastChangeFrame = map.biasLastChangeOnFrame(); + if(shard.bias.lastUpdateFrame == lastChangeFrame) + return; + + shard.bias.lastUpdateFrame = lastChangeFrame; + + shard.bias.tracker.clearContributors(); + + Surface const &surface = seg.lineSide().middle(); + Vector2d const &from = seg.hedge().origin(); + Vector2d const &to = seg.hedge().twin().origin(); + Vector2d const center = (from + to) / 2; + + foreach(BiasSource *source, map.biasSources()) + { + // If the source is too weak we will ignore it completely. + if(source->intensity() <= 0) + continue; + + 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(); + if(k == 0 || len < distance) + distance = len; + } + + if(sourceToSurface.dot(surface.normal()) < 0) + continue; + + shard.bias.tracker.addContributor(source, source->evaluateIntensity() / de::max(distance, 1.0)); + } + } + + /* + void applyBiasDigestToGeometryGroups(MapElement &mapElement, BiasDigest &changes) + { + GeometryGroups::iterator found = geomGroups.find(&mapElement); + if(found != geomGroups.end()) + { + DENG2_FOR_EACH(Shards, shard, *found) + { + shard->bias.tracker.applyChanges(changes); + } + } + }*/ + void addReverbBspLeaf(BspLeaf *bspLeaf) { if(!bspLeaf) return; @@ -802,6 +1015,15 @@ DENG2_PIMPL(SectorCluster) reverb[SRD_VOLUME] = 1; } + /// Observes Plane HeightSmoothedChange. + void planeHeightSmoothedChanged(Plane &plane) + { + markDependantSurfacesForDecorationUpdate(); + + // We may need to update one or both mapped planes. + maybeInvalidateMapping(plane.indexInSector()); + } + /// Observes Sector LightLevelChange. void sectorLightLevelChanged(Sector §or) { @@ -982,40 +1204,6 @@ bool SectorCluster::hasSkyMaskedPlane() const return false; } -static void applyBiasDigestToWallSections(HEdge *hedge, BiasDigest &changes) -{ - if(!hedge || !hedge->hasMapElement()) - return; - hedge->mapElementAs().applyBiasDigest(changes); -} - -void SectorCluster::applyBiasDigest(BiasDigest &changes) -{ - foreach(BspLeaf *bspLeaf, d->bspLeafs) - { - bspLeaf->applyBiasDigest(changes); - - HEdge *base = bspLeaf->poly().hedge(); - HEdge *hedge = base; - do - { - applyBiasDigestToWallSections(hedge, changes); - } while((hedge = &hedge->next()) != base); - - foreach(Mesh *mesh, bspLeaf->extraMeshes()) - foreach(HEdge *hedge, mesh->hedges()) - { - applyBiasDigestToWallSections(hedge, changes); - } - - foreach(Polyobj *polyobj, bspLeaf->polyobjs()) - foreach(HEdge *hedge, polyobj->mesh().hedges()) - { - applyBiasDigestToWallSections(hedge, changes); - } - } -} - SectorCluster::LightId SectorCluster::lightSourceId() const { /// @todo Need unique cluster ids. @@ -1060,8 +1248,118 @@ int SectorCluster::blockLightSourceZBias() return 0; } +void SectorCluster::applyBiasDigest(BiasDigest &changes) +{ + DENG2_FOR_EACH(Instance::GeometryGroups, geomGroup, d->geomGroups) + DENG2_FOR_EACH(Instance::Shards, shard, *geomGroup) + { + shard->bias.tracker.applyChanges(changes); + } + + /*foreach(BspLeaf *bspLeaf, d->bspLeafs) + { + d->applyBiasDigestToGeometryGroups(*bspLeaf, changes); + + HEdge *base = bspLeaf->poly().hedge(); + HEdge *hedge = base; + do + { + if(hedge->hasMapElement()) + { + d->applyBiasDigestToGeometryGroups(hedge->mapElement(), changes); + } + } while((hedge = &hedge->next()) != base); + + foreach(Mesh *mesh, bspLeaf->extraMeshes()) + foreach(HEdge *hedge, mesh->hedges()) + { + if(hedge->hasMapElement()) + { + d->applyBiasDigestToGeometryGroups(hedge->mapElement(), changes); + } + } + + foreach(Polyobj *polyobj, bspLeaf->polyobjs()) + foreach(HEdge *hedge, polyobj->mesh().hedges()) + { + if(hedge->hasMapElement()) + { + d->applyBiasDigestToGeometryGroups(hedge->mapElement(), changes); + } + } + }*/ +} + +void SectorCluster::applyBiasLightSources(MapElement &mapElement, int geomId, + Vector3f const *posCoords, Vector4f *colorCoords) +{ + DENG2_ASSERT(posCoords != 0 && colorCoords != 0); + + Map &map = mapElement.map(); + uint const biasTime = map.biasCurrentTime(); + + Shard *shard = d->shard(mapElement, geomId); + DENG2_ASSERT(shard != 0); + + Surface const *surface = 0; + switch(mapElement.type()) + { + case DMU_BSPLEAF: { + BspLeaf &bspLeaf = mapElement.as(); + + // Should we update? + if(devUpdateBiasContributors) + { + d->updateBiasContributors(*shard, bspLeaf, geomId); + } + + surface = &visPlane(geomId).surface(); + break; } + + case DMU_SEGMENT: { + LineSideSegment &seg = mapElement.as(); + + // Should we update? + if(devUpdateBiasContributors) + { + d->updateBiasContributors(*shard, seg, geomId); + } + + surface = &seg.lineSide().surface(geomId); + break; } + + default: break; + } + + Vector3f const *posIt = posCoords; + Vector4f *colorIt = colorCoords; + for(int i = 0; i < shard->bias.illums.count(); ++i, posIt++, colorIt++) + { + *colorIt += shard->bias.illums[i].evaluate(*posIt, surface->normal(), biasTime); + } + + // Any changes from contributors will have now been applied. + shard->bias.tracker.markIllumUpdateCompleted(); +} + +void SectorCluster::updateBiasAfterGeometryMove(MapElement &mapElement, int geomId) +{ + if(Shard *shard = d->shard(mapElement, geomId, false /*don't allocate*/)) + { + shard->bias.tracker.updateAllContributors(); + } +} + #endif // __CLIENT__ +void SectorCluster::consoleRegister() // static +{ +#ifdef __CLIENT__ + // Development variables. + C_VAR_INT("rend-dev-bias-affected", &devUpdateBiasContributors, CVF_NO_ARCHIVE, 0, 1); +#endif +} + // SectorClusterCirculator ----------------------------------------------------- SectorCluster *SectorClusterCirculator::getCluster(HEdge const &hedge) // static