From 47c0930d33f94e9ceeaf3ea22527fa949cd49dbe Mon Sep 17 00:00:00 2001 From: danij Date: Mon, 29 Jul 2013 20:31:45 +0100 Subject: [PATCH] Refactor|Shadow Bias: Moved bias source affection updates out of BiasSurface Affection is the contribution of light from source to "surface" in the same map. To determine affection demands knowledge of the origin of the both source and surface. BiasSurface lacks map surface origin knowledge therefore it cannot perform affection determination itself unaided. Source affection is now performed using the half-edge geometry and by the map surface which owns the BiasSource. Also, bias sources are now correctly occluded when in the void and no longer result in severe performance degredation (caused by performing unnecessary lighting calculations vs every map surface in range). A few bugs were fixed and the implementation cleaned of all residual aftermath from previous development/refactoring efforts. Note that the present mechanisms for storing light contributions and affections have been replaced with the simplest mechanism that works for the sake of clarity. A future implementation of the bias lighting model will most probably need to replace these in any case. --- doomsday/client/include/render/biassource.h | 8 +- doomsday/client/include/render/biassurface.h | 33 +- doomsday/client/include/world/bspleaf.h | 3 + doomsday/client/include/world/segment.h | 9 + doomsday/client/src/render/biassource.cpp | 64 +-- doomsday/client/src/render/biassurface.cpp | 406 +++++++------------ doomsday/client/src/render/rend_main.cpp | 54 ++- doomsday/client/src/world/bspleaf.cpp | 65 ++- doomsday/client/src/world/segment.cpp | 66 ++- 9 files changed, 375 insertions(+), 333 deletions(-) diff --git a/doomsday/client/include/render/biassource.h b/doomsday/client/include/render/biassource.h index 51eb5db037..3e040eaf4c 100644 --- a/doomsday/client/include/render/biassource.h +++ b/doomsday/client/include/render/biassource.h @@ -103,7 +103,7 @@ class BiasSource : public Grabbable, public de::ISerializable BspLeaf &bspLeafAtOrigin() const; /** - * Returns the light intensity multiplier for the source. The + * Returns the "primary" light intensity multiplier for the source. The * IntensityChange audience is notified whenever the intensity changes. * * @see setIntensity() @@ -122,6 +122,12 @@ class BiasSource : public Grabbable, public de::ISerializable */ BiasSource &setIntensity(float newIntensity); + /** + * Determine the effective light intensity for the source and factoring in + * sector light level multipliers/scale-factors. + */ + float evaluateIntensity() const; + /** * Returns the light color strength factors for the source. The ColorChange * audience is notified whenever the color changes. diff --git a/doomsday/client/include/render/biassurface.h b/doomsday/client/include/render/biassurface.h index db3e5dd982..27e1269f54 100644 --- a/doomsday/client/include/render/biassurface.h +++ b/doomsday/client/include/render/biassurface.h @@ -24,6 +24,7 @@ #include "MapElement" +class BiasSource; class BiasTracker; /** @@ -36,33 +37,37 @@ class BiasSurface /** * Construct a new surface. * - * @param owner Map element which will own the surface (either a - * BspLeaf or a Segment). - * @param subElemIndex Index for the subelement of @a owner. * @param size Number of vertices. */ - BiasSurface(de::MapElement &owner, int subElemIndex, int size); + BiasSurface(int size); /** * To be called to register the commands and variables of this module. */ static void consoleRegister(); + uint lastUpdateOnFrame() const; + + void setLastUpdateOnFrame(uint newLastUpdateFrameNumber); + + void clearAffected(); + + void addAffected(float intensity, BiasSource *source); + + void updateAffection(BiasTracker &changes); + + void updateAfterMove(); + /** * Perform lighting for the supplied geometry. It is assumed that this * geometry has the @em same number of vertices as the bias surface. * - * @param colors Array of colors to be written to. - * @param verts Array of vertices to be lit. - * @param vertCount Number of vertices (in the array) to be lit. - * @param sectorLightLevel Sector light level. + * @param vertCount Number of vertices to be lit. + * @param positions World coordinates for each vertex. + * @param colors Final lighting values will be written here. */ - void lightPoly(struct ColorRawf_s *colors, struct rvertex_s const *verts, - int vertCount, float sectorLightLevel); - - void updateAfterMove(); - - void updateAffection(BiasTracker &changes); + void lightPoly(de::Vector3f const &surfaceNormal, int vertCount, + struct rvertex_s const *positions, struct ColorRawf_s *colors); private: DENG2_PRIVATE(d) diff --git a/doomsday/client/include/world/bspleaf.h b/doomsday/client/include/world/bspleaf.h index 9e2ec1f038..7151fffc79 100644 --- a/doomsday/client/include/world/bspleaf.h +++ b/doomsday/client/include/world/bspleaf.h @@ -285,6 +285,9 @@ class BspLeaf : public de::MapElement */ void updateBiasAffection(BiasTracker &changes); + void lightPoly(int group, int vertCount, struct rvertex_s const *positions, + struct ColorRawf_s *colors); + /** * Returns a pointer to the first ShadowLink; otherwise @c 0. */ diff --git a/doomsday/client/include/world/segment.h b/doomsday/client/include/world/segment.h index 7a4c606002..7bc128c531 100644 --- a/doomsday/client/include/world/segment.h +++ b/doomsday/client/include/world/segment.h @@ -77,6 +77,12 @@ class Segment : public de::MapElement inline Vertex &to() const { return hedge().twin().vertex(); } + /** + * Returns the point on the line segment which lies at the exact center of + * the two vertexes. + */ + inline de::Vector2d center() const { return (from().origin() + to().origin()) / 2; } + /** * Returns @c true iff a polygon attributed to a BSP leaf is associated * with the line segment. @@ -243,6 +249,9 @@ class Segment : public de::MapElement */ void updateBiasAffection(BiasTracker &changes); + void lightPoly(int group, int vertCount, struct rvertex_s const *positions, + struct ColorRawf_s *colors); + #endif // __CLIENT__ protected: diff --git a/doomsday/client/src/render/biassource.cpp b/doomsday/client/src/render/biassource.cpp index 530ccce148..6b530f5887 100644 --- a/doomsday/client/src/render/biassource.cpp +++ b/doomsday/client/src/render/biassource.cpp @@ -36,8 +36,9 @@ DENG2_PIMPL(BiasSource) /// Origin of the source in the map coordinate space. Vector3d origin; - /// BSP leaf at this origin. + /// BSP leaf at the origin. BspLeaf *bspLeaf; + bool inVoid; ///< Set to @c true if the origin is in the void. /// Intensity of the emitted light. float primaryIntensity; @@ -62,6 +63,7 @@ DENG2_PIMPL(BiasSource) : Base(i), origin(origin), bspLeaf(0), + inVoid(true), primaryIntensity(intensity), intensity(intensity), color(color), @@ -75,6 +77,7 @@ DENG2_PIMPL(BiasSource) : Base(i), origin(other.origin), bspLeaf(other.bspLeaf), + inVoid(other.inVoid), primaryIntensity(other.primaryIntensity), intensity(other.intensity), color(other.color), @@ -115,6 +118,14 @@ DENG2_PIMPL(BiasSource) i->biasSourceColorChanged(self, oldColor, changedComponents); } } + + void updateBspLocation() + { + if(bspLeaf) return; + /// @todo Do not assume the current map. + bspLeaf = &App_World().map().bspLeafAt(origin); + inVoid = !(bspLeaf->pointInside(origin)); + } }; BiasSource::BiasSource(Vector3d const &origin, float intensity, Vector3f const &color, @@ -156,11 +167,7 @@ void BiasSource::setOrigin(Vector3d const &newOrigin) BspLeaf &BiasSource::bspLeafAtOrigin() const { - if(!d->bspLeaf) - { - /// @todo Do not assume the current map. - d->bspLeaf = &App_World().map().bspLeafAt(d->origin); - } + d->updateBspLocation(); return *d->bspLeaf; } @@ -169,9 +176,15 @@ float BiasSource::intensity() const return d->primaryIntensity; } +float BiasSource::evaluateIntensity() const +{ + d->updateBspLocation(); + return d->inVoid? 0 : d->intensity; +} + BiasSource &BiasSource::setIntensity(float newIntensity) { - if(!de::fequal(d->intensity, newIntensity)) + if(!de::fequal(d->primaryIntensity, newIntensity)) { float oldIntensity = d->primaryIntensity; @@ -251,29 +264,32 @@ bool BiasSource::trackChanges(BiasTracker &changes, uint indexInTracker, uint cu { if(d->maxLight > 0 || d->minLight > 0) { - float const oldIntensity = d->intensity; + float const oldIntensity = intensity(); + float newIntensity = 0; - /// @todo Should observe Sector::LightLevelChange - Sector const §or = bspLeafAtOrigin().sector(); - - // Lower intensities are useless for light emission. - if(sector.lightLevel() >= d->maxLight) + if(!d->inVoid) { - d->intensity = d->primaryIntensity; + /// @todo Should observe Sector::LightLevelChange + Sector const §or = d->bspLeaf->sector(); + + // Lower intensities are useless for light emission. + if(sector.lightLevel() >= d->maxLight) + { + newIntensity = d->primaryIntensity; + } + + if(sector.lightLevel() >= d->minLight && d->minLight != d->maxLight) + { + newIntensity = d->primaryIntensity * + (sector.lightLevel() - d->minLight) / (d->maxLight - d->minLight); + } } - if(sector.lightLevel() >= d->minLight && d->minLight != d->maxLight) - { - d->intensity = d->primaryIntensity * - (sector.lightLevel() - d->minLight) / (d->maxLight - d->minLight); - } - else + if(newIntensity != oldIntensity) { - d->intensity = 0; - } - - if(d->intensity != oldIntensity) + d->intensity = newIntensity; d->changed = true; + } } if(!d->changed) return false; diff --git a/doomsday/client/src/render/biassurface.cpp b/doomsday/client/src/render/biassurface.cpp index 8014ea7ab6..4bafda0c8f 100644 --- a/doomsday/client/src/render/biassurface.cpp +++ b/doomsday/client/src/render/biassurface.cpp @@ -17,15 +17,14 @@ * http://www.gnu.org/licenses */ +#include #include #include "de_base.h" #include "de_console.h" -#include "world/map.h" -#include "world/linesighttest.h" #include "BspLeaf" -#include "Segment" +#include "world/linesighttest.h" #include "BiasTracker" #include "render/rendpoly.h" @@ -34,23 +33,24 @@ using namespace de; -/// Ignore light intensities below this threshold when accumulating sources. +/// Ignore intensities below this threshold when accumulating contributions. static float const MIN_INTENSITY = .005f; /// Maximum number of sources which can contribute light to a vertex. static int const MAX_AFFECTED = 6; -static float lightMin = .85f; //cvar -static float lightMax = 1.f; //cvar static int lightSpeed = 130; //cvar static int devUpdateAffected = true; //cvar static int devUseSightCheck = true; //cvar -struct BiasAffection +struct Contributor { - int sourceIndex; + BiasSource *source; }; +/// Contribution intensity => bias source. +typedef QMap Affection; + /** * Per-vertex illumination data. */ @@ -62,8 +62,8 @@ struct VertexIllum /// Interpolation is in progress. Interpolating = 0x1, - /// Vertex is still unseen (color is unknown). - StillUnseen = 0x2 + /// Vertex is unseen (color is unknown). + Unseen = 0x2 }; Q_DECLARE_FLAGS(Flags, Flag) @@ -72,7 +72,7 @@ struct VertexIllum */ struct Contribution { - int sourceIndex; ///< Index of the source. + BiasSource *source; ///< The contributing light source. Vector3f color; ///< The contributed light intensity. }; @@ -82,11 +82,11 @@ struct VertexIllum Flags flags; Contribution casted[MAX_AFFECTED]; - VertexIllum() : updateTime(0), flags(StillUnseen) + VertexIllum() : updateTime(0), flags(Unseen) { for(int i = 0; i < MAX_AFFECTED; ++i) { - casted[i].sourceIndex = -1; + casted[i].source = 0; } } @@ -120,12 +120,14 @@ struct VertexIllum /** * @return Light contribution by the specified source. */ - Vector3f &contribution(int sourceIndex, BiasAffection *const affectedSources) + Vector3f &contribution(BiasSource *source, Affection const &affectedSources) { + DENG2_ASSERT(source != 0); + // Do we already have a contribution for this source? for(int i = 0; i < MAX_AFFECTED; ++i) { - if(casted[i].sourceIndex == sourceIndex) + if(casted[i].source == source) return casted[i].color; } @@ -133,242 +135,107 @@ struct VertexIllum for(int i = 0; i < MAX_AFFECTED; ++i) { bool inUse = false; - for(int k = 0; k < MAX_AFFECTED; ++k) - { - if(affectedSources[k].sourceIndex < 0) - break; - if(affectedSources[k].sourceIndex == casted[i].sourceIndex) + if(casted[i].source) + { + foreach(Contributor const &ctbr, affectedSources) { - inUse = true; - break; + if(ctbr.source == casted[i].source) + { + inUse = true; + break; + } } } if(!inUse) { // This will do nicely. - casted[i].sourceIndex = sourceIndex; - casted[i].color = Vector3f(); + casted[i].source = source; + casted[i].color = Vector3f(); return casted[i].color; } } // Now how'd that happen? - throw Error("VertexIllum::casted", QString("No light emitted by source #%1").arg(sourceIndex)); + throw Error("VertexIllum::casted", QString("No light emitted by source")); } }; Q_DECLARE_OPERATORS_FOR_FLAGS(VertexIllum::Flags) -static inline void addLight(Vector3f &dest, Vector3f const &color, float howMuch = 1.0f) -{ - dest = (dest + color * howMuch).min(Vector3f(1, 1, 1)); -} - /** * evalLighting uses these -- they must be set before it is called. */ -static float biasAmount; +static uint biasTime; +static MapElement const *bspRoot; +static Vector3f const *mapSurfaceNormal; static BiasTracker trackChanged; static BiasTracker trackApplied; DENG2_PIMPL_NOREF(BiasSurface) { - struct Affection - { - float intensities[MAX_AFFECTED]; // init unnecessary - int numFound; - - Affection() : numFound(0) {} - - int findWeakest() const - { - int weakest = 0; - for(int i = 1; i < MAX_AFFECTED; ++i) - { - if(intensities[i] < intensities[weakest]) - weakest = i; - } - return weakest; - } - }; - - MapElement &owner; - - int subElemIndex; - QVector illums; /// @todo use std::vector instead? BiasTracker tracker; - BiasAffection affected[MAX_AFFECTED]; + Affection affected; uint lastUpdateOnFrame; - Instance(MapElement &owner, int subElemIndex, int size) - : owner(owner), - subElemIndex(subElemIndex), - illums(size), - lastUpdateOnFrame(0) + Instance(int size) : illums(size), lastUpdateOnFrame(0) {} - inline Surface &mapSurface() - { - if(owner.type() == DMU_SEGMENT) - return owner.as()->lineSide().middle(); - // Must be a BspLeaf, then. - return owner.as()->sector().plane(subElemIndex).surface(); - } - - inline void clearAffected() - { - std::memset(affected, -1, sizeof(affected)); - } - - void addAffected(int sourceIndex, float intensity, Affection &aff) - { - if(aff.numFound < MAX_AFFECTED) - { - affected[aff.numFound].sourceIndex = sourceIndex; - aff.intensities[aff.numFound] = intensity; - aff.numFound++; - } - else - { - // Drop the weakest. - int weakest = aff.findWeakest(); - - affected[weakest].sourceIndex = sourceIndex; - aff.intensities[weakest] = intensity; - } - } - - void updateAffected(Vector2d const &from, Vector2d const &to, - Vector3f const &surfaceNormal) - { - DENG_ASSERT(illums.count() == 4); - - clearAffected(); - - Affection aff; - foreach(BiasSource *src, owner.map().biasSources()) - { - // Is the source is too weak, ignore it entirely. - if(src->intensity() <= MIN_INTENSITY) - continue; - - // Calculate minimum 2D distance to the segment. - Vector2d delta; - float distance = 0; - for(int k = 0; k < 2; ++k) - { - delta = (!k? from : to) - Vector2d(src->origin()); - float len = delta.length(); - if(k == 0 || len < distance) - distance = len; - } - - if(delta.normalize().dot(surfaceNormal) >= 0) - continue; - - float intensity = src->intensity() / de::max(distance, 1.f); - if(intensity < MIN_INTENSITY) - continue; - - addAffected(owner.map().toIndex(*src), intensity, aff); - } - } - - void updateAffected(rvertex_t const *verts, int vertCount, - Vector3d const &surfacePoint, Vector3f const &surfaceNormal) - { - DENG_ASSERT(illums.count() == vertCount && verts != 0); - - clearAffected(); - - Affection aff; - foreach(BiasSource *src, owner.map().biasSources()) - { - // Is the source is too weak, ignore it entirely. - if(src->intensity() <= MIN_INTENSITY) - continue; - - // Calculate minimum 2D distance to the BSP leaf. - /// @todo This is probably too accurate an estimate. - Vector3d delta(surfacePoint - src->origin()); - float distance = 0; - for(int k = 0; k < illums.count(); ++k) - { - Vector2d const vertPos = Vector2d(verts[k].pos[VX], verts[k].pos[VY]); - - float len = (vertPos - Vector2d(src->origin())).length(); - if(k == 0 || len < distance) - distance = len; - } - - if(delta.normalize().dot(surfaceNormal) >= 0) - continue; - - float intensity = src->intensity() / de::max(distance, 1.f); - if(intensity < MIN_INTENSITY) - continue; - - addAffected(owner.map().toIndex(*src), intensity, aff); - } - } - /** * Perform lighting calculations for the specified @a mapVertexOrigin. * * @todo Only recalculate the changed lights (colors contributed by * others can be stored in the "affected" array. */ - Vector3f evalLighting(VertexIllum &vi, Vector3d const &mapVertexOrigin, - Vector3f const &mapSurfaceNormal) + Vector3f evalLighting(VertexIllum &vi, Vector3d const &surfacePoint) { -#define COLOR_CHANGE_THRESHOLD 0.1f +#define COLOR_CHANGE_THRESHOLD 0.1f // Ignore small variations for perf - uint currentTime = owner.map().biasCurrentTime(); + static Vector3f const saturated(1, 1, 1); + + Map &map = bspRoot->map(); - bool illumChanged = false; uint latestSourceUpdate = 0; - struct { - int index; - BiasSource *source; - BiasAffection *affection; - bool changed; - } affecting[MAX_AFFECTED + 1], *aff; + bool illumChanged = false; // Lighting must be fully evaluated the first time. - if(vi.flags & VertexIllum::StillUnseen) + if(vi.flags & VertexIllum::Unseen) { illumChanged = true; - vi.flags &= ~VertexIllum::StillUnseen; + vi.flags &= ~VertexIllum::Unseen; } // Determine if any affecting sources have changed since last frame. - aff = affecting; - if(owner.map().biasSourceCount() > 0) + struct { + BiasSource *source; + bool changed; + } affecting[MAX_AFFECTED + 1], *aff = affecting; + + //if(map.biasSourceCount() > 0) { - for(int i = 0; affected[i].sourceIndex >= 0 && i < MAX_AFFECTED; ++i) + foreach(Contributor const &ctbr, affected) { - int idx = affected[i].sourceIndex; - // Is this a valid index? - if(idx < 0 || idx >= owner.map().biasSourceCount()) + /*if(sourceIndex < 0 || sourceIndex >= map.biasSourceCount()) + { + illumChanged = true; continue; + }*/ - aff->index = idx; - aff->source = owner.map().biasSource(idx); - aff->affection = &affected[i]; + int sourceIndex = map.toIndex(*ctbr.source); + aff->source = ctbr.source; - if(trackChanged.check(idx)) + if(trackChanged.check(sourceIndex)) { aff->changed = true; illumChanged = true; - trackApplied.mark(idx); + trackApplied.mark(sourceIndex); // Remember the earliest time an affecting source changed. if(latestSourceUpdate < aff->source->lastUpdateTime()) @@ -387,13 +254,12 @@ DENG2_PIMPL_NOREF(BiasSurface) } aff->source = 0; - Vector3f color; if(illumChanged) { // Recalculate the contribution for each light. for(aff = affecting; aff->source; aff++) { - if(!aff->changed) //tracker.check(aff->index)) + if(!aff->changed) { // We can reuse the previously calculated value. This // can only be done if this particular source hasn't @@ -401,13 +267,26 @@ DENG2_PIMPL_NOREF(BiasSurface) continue; } - BiasSource *s = aff->source; + BiasSource *source = aff->source; + Vector3f &casted = vi.contribution(source, affected); + + /// @todo LineSightTest should (optionally) perform this test. + Sector *sector = &source->bspLeafAtOrigin().sector(); + if((!sector->floor().surface().hasSkyMaskedMaterial() && + source->origin().z < sector->floor().visHeight()) || + (!sector->ceiling().surface().hasSkyMaskedMaterial() && + source->origin().z > sector->ceiling().visHeight())) + { + // This affecting source does not contribute any light. + casted = Vector3f(); + continue; + } - Vector3f &casted = vi.contribution(aff->index, affected); - Vector3d delta = s->origin() - mapVertexOrigin; + Vector3d sourceToSurface = source->origin() - surfacePoint; if(devUseSightCheck && - !LineSightTest(s->origin(), mapVertexOrigin + delta / 100).trace(owner.map().bspRoot())) + !LineSightTest(source->origin(), + surfacePoint + sourceToSurface / 100).trace(*bspRoot)) { // LOS fail. // This affecting source does not contribute any light. @@ -415,29 +294,28 @@ DENG2_PIMPL_NOREF(BiasSurface) continue; } - double distance = delta.length(); - double dot = delta.normalize().dot(mapSurfaceNormal); + double distance = sourceToSurface.length(); + double dot = sourceToSurface.normalize().dot(*mapSurfaceNormal); // The surface faces away from the light? - if(dot <= 0) + if(dot < 0) { casted = Vector3f(); continue; } // Apply light casted from this source. - float strength = de::clamp(0.f, float( dot * s->intensity() / distance ), 1.f); - casted = s->color() * strength; + float strength = dot * source->evaluateIntensity() / distance; + casted = source->color() * de::clamp(0.f, strength, 1.f); } /* * Accumulate light contributions from each affecting source. */ Vector3f newColor; // Initial color is black. - Vector3f const saturated(1, 1, 1); for(aff = affecting; aff->source; aff++) { - newColor += vi.contribution(aff->index, affected); + newColor += vi.contribution(aff->source, affected); // Stop once fully saturated. if(newColor >= saturated) @@ -454,7 +332,7 @@ DENG2_PIMPL_NOREF(BiasSurface) if(vi.flags & VertexIllum::Interpolating) { // Must not lose the half-way interpolation. - Vector3f mid; vi.lerp(mid, currentTime); + Vector3f mid; vi.lerp(mid, biasTime); // This is current color at this very moment. vi.color = mid; @@ -468,12 +346,14 @@ DENG2_PIMPL_NOREF(BiasSurface) } // Finalize lighting (i.e., perform interpolation if needed). - vi.lerp(color, currentTime); + Vector3f color; + vi.lerp(color, biasTime); // Apply an ambient light term? - if(owner.map().hasLightGrid()) + if(map.hasLightGrid()) { - addLight(color, owner.map().lightGrid().evaluate(mapVertexOrigin)); + color += map.lightGrid().evaluate(surfacePoint) + .min(saturated); // clamp } return color; @@ -482,27 +362,50 @@ DENG2_PIMPL_NOREF(BiasSurface) } }; -BiasSurface::BiasSurface(MapElement &owner, int subElemIndex, int size) - : d(new Instance(owner, subElemIndex, size)) +BiasSurface::BiasSurface(int size) : d(new Instance(size)) {} void BiasSurface::consoleRegister() // static { - C_VAR_FLOAT ("rend-bias-min", &lightMin, 0, 0, 1); - C_VAR_FLOAT ("rend-bias-max", &lightMax, 0, 0, 1); - C_VAR_INT ("rend-bias-lightspeed", &lightSpeed, 0, 0, 5000); + C_VAR_INT("rend-bias-lightspeed", &lightSpeed, 0, 0, 5000); // Development variables. - C_VAR_INT ("rend-dev-bias-affected", &devUpdateAffected, CVF_NO_ARCHIVE, 0, 1); - C_VAR_INT ("rend-dev-bias-sight", &devUseSightCheck, CVF_NO_ARCHIVE, 0, 1); + C_VAR_INT("rend-dev-bias-affected", &devUpdateAffected, CVF_NO_ARCHIVE, 0, 1); + C_VAR_INT("rend-dev-bias-sight", &devUseSightCheck, CVF_NO_ARCHIVE, 0, 1); } -void BiasSurface::updateAfterMove() +uint BiasSurface::lastUpdateOnFrame() const +{ + return d->lastUpdateOnFrame; +} + +void BiasSurface::setLastUpdateOnFrame(uint newLastUpdateFrameNumber) +{ + d->lastUpdateOnFrame = newLastUpdateFrameNumber; +} + +void BiasSurface::clearAffected() { - for(int i = 0; i < MAX_AFFECTED && d->affected[i].sourceIndex >= 0; ++i) + d->affected.clear(); +} + +void BiasSurface::addAffected(float intensity, BiasSource *source) +{ + if(!source) return; + + // If its too weak we will ignore it entirely. + if(intensity < MIN_INTENSITY) return; + + if(d->affected.count() == MAX_AFFECTED) { - d->owner.map().biasSource(d->affected[i].sourceIndex)->forceUpdate(); + // Drop the weakest. + Affection::Iterator weakest = d->affected.begin(); + if(intensity <= weakest.key()) return; + d->affected.remove(weakest.key()); } + + Contributor ctbr = { source }; + d->affected.insert(intensity, ctbr); } void BiasSurface::updateAffection(BiasTracker &changes) @@ -512,12 +415,9 @@ void BiasSurface::updateAffection(BiasTracker &changes) // Determine whether these changes affect us. bool needApplyChanges = false; - for(int i = 0; i < MAX_AFFECTED; ++i) + foreach(Contributor const &ctbr, d->affected) { - if(d->affected[i].sourceIndex < 0) - break; - - if(changes.check(d->affected[i].sourceIndex)) + if(changes.check(App_World().map().toIndex(*ctbr.source))) { needApplyChanges = true; break; @@ -529,67 +429,33 @@ void BiasSurface::updateAffection(BiasTracker &changes) // Mark the illumination unseen to force an update. for(int i = 0; i < d->illums.size(); ++i) { - d->illums[i].flags |= VertexIllum::StillUnseen; + d->illums[i].flags |= VertexIllum::Unseen; } } -void BiasSurface::lightPoly(struct ColorRawf_s *colors, - struct rvertex_s const *verts, int vertCount, float sectorLightLevel) +void BiasSurface::updateAfterMove() { - // Apply sectorlight bias (distance darkening is not a factor). - if(sectorLightLevel > lightMin && lightMax > lightMin) - { - biasAmount = (sectorLightLevel - lightMin) / (lightMax - lightMin); - - if(biasAmount > 1) - biasAmount = 1; - } - else + foreach(Contributor const &ctbr, d->affected) { - biasAmount = 0; + ctbr.source->forceUpdate(); } +} +void BiasSurface::lightPoly(Vector3f const &surfaceNormal, int vertCount, + rvertex_t const *positions, ColorRawf *colors) +{ + // Configure global arguments for evalLighting(), for perf + bspRoot = &App_World().map().bspRoot(); + biasTime = bspRoot->map().biasCurrentTime(); + mapSurfaceNormal = &surfaceNormal; trackChanged = d->tracker; trackApplied.clear(); - Vector3f const &mapSurfaceNormal = d->mapSurface().normal(); - - // Have any of the affected lights changed? - if(devUpdateAffected) - { - // If the data is already up to date, nothing needs to be done. - if(d->lastUpdateOnFrame != d->owner.map().biasLastChangeOnFrame()) - { - d->lastUpdateOnFrame = d->owner.map().biasLastChangeOnFrame(); - - /* - * @todo This could be enhanced so that only the lights on the right - * side of the surface are taken into consideration. - */ - if(d->owner.type() == DMU_SEGMENT) - { - Segment const *segment = d->owner.as(); - - d->updateAffected(segment->from().origin(), segment->to().origin(), - mapSurfaceNormal); - } - else - { - BspLeaf const *bspLeaf = d->owner.as(); - Plane const &plane = bspLeaf->sector().plane(d->subElemIndex); - - Vector3d surfacePoint(bspLeaf->poly().center(), plane.visHeight()); - - d->updateAffected(verts, vertCount, surfacePoint, mapSurfaceNormal); - } - } - } - - int i; rvertex_t const *vtx = verts; + int i; rvertex_t const *vtx = positions; for(i = 0; i < vertCount; ++i, vtx++) { - Vector3d mapVertexOrigin(vtx->pos[VX], vtx->pos[VY], vtx->pos[VZ]); - Vector3f light = d->evalLighting(d->illums[i], mapVertexOrigin, mapSurfaceNormal); + Vector3d origin(vtx->pos[VX], vtx->pos[VY], vtx->pos[VZ]); + Vector3f light = d->evalLighting(d->illums[i], origin); for(int c = 0; c < 3; ++c) colors[i].rgba[c] = light[c]; diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index ba7e8fad9e..e3790f825f 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -733,7 +733,6 @@ static void flatShinyTexCoords(rtexcoord_t *tc, float const xyz[3]) struct rendworldpoly_params_t { - bool isWall; int flags; /// @ref rendpolyFlags blendmode_t blendMode; Vector3d const *texTL; @@ -749,7 +748,8 @@ struct rendworldpoly_params_t uint shadowListIdx; // List of shadows that affect this poly. float glowing; bool forceOpaque; - BiasSurface *bsuf; + MapElement *elem; + int subElemIndex; // Wall only: struct { @@ -765,8 +765,10 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, { DENG_ASSERT(rvertices != 0); - uint const realNumVertices = (p.isWall? 3 + p.wall.leftEdge->divisionCount() + 3 + p.wall.rightEdge->divisionCount() : numVertices); - bool const mustSubdivide = (p.isWall && (p.wall.leftEdge->divisionCount() || p.wall.rightEdge->divisionCount())); + bool const isWall = p.elem->type() == DMU_SEGMENT; + + uint const realNumVertices = (isWall? 3 + p.wall.leftEdge->divisionCount() + 3 + p.wall.rightEdge->divisionCount() : numVertices); + bool const mustSubdivide = (isWall && (p.wall.leftEdge->divisionCount() || p.wall.rightEdge->divisionCount())); bool const skyMaskedMaterial = ((p.flags & RPF_SKYMASK) || (ms.material().isSkyMasked())); bool const drawAsVisSprite = (!p.forceOpaque && !(p.flags & RPF_SKYMASK) && (!ms.isOpaque() || p.alpha < 1 || p.blendMode > 0)); @@ -834,7 +836,7 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, } } - if(p.isWall) + if(isWall) { // Primary texture coordinates. quadTexCoords(primaryCoords, rvertices, p.wall.sectionWidth, *p.texTL); @@ -910,11 +912,19 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, else { // Non-uniform color. - if(useBias && p.bsuf) + if(useBias) { // Do BIAS lighting for this poly. - p.bsuf->lightPoly(rcolors, rvertices, int(numVertices), - currentSectorLightLevel); + if(p.elem->type() == DMU_BSPLEAF) + { + p.elem->as()-> + lightPoly(p.subElemIndex, int(numVertices), rvertices, rcolors); + } + else + { + p.elem->as()-> + lightPoly(p.subElemIndex, int(numVertices), rvertices, rcolors); + } if(p.glowing > 0) { @@ -937,7 +947,7 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, // Blend sector light+color+surfacecolor Vector3f vColor = (*p.surfaceColor) * currentSectorLightColor; - if(p.isWall && llL != llR) + if(isWall && llL != llR) { lightVertex(rcolors[0], rvertices[0], llL, vColor); lightVertex(rcolors[1], rvertices[1], llL, vColor); @@ -952,7 +962,7 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, else { // Use sector light+color only. - if(p.isWall && llL != llR) + if(isWall && llL != llR) { lightVertex(rcolors[0], rvertices[0], llL, currentSectorLightColor); lightVertex(rcolors[1], rvertices[1], llL, currentSectorLightColor); @@ -966,7 +976,7 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, } // Bottom color (if different from top)? - if(p.isWall && p.wall.surfaceColor2) + if(isWall && p.wall.surfaceColor2) { // Blend sector light+color+surfacecolor Vector3f vColor = (*p.wall.surfaceColor2) * currentSectorLightColor; @@ -1029,7 +1039,7 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, if(drawAsVisSprite) { - DENG_ASSERT(p.isWall); + DENG_ASSERT(isWall); /** * Masked polys (walls) get a special treatment (=> vissprite). @@ -1061,8 +1071,8 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, parm.lastIdx = 0; parm.texTL = p.texTL; parm.texBR = p.texBR; - parm.isWall = p.isWall; - if(p.isWall) + parm.isWall = isWall; + if(parm.isWall) { parm.wall.leftEdge = p.wall.leftEdge; parm.wall.rightEdge = p.wall.rightEdge; @@ -1082,8 +1092,8 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, parm.realNumVertices = realNumVertices; parm.texTL = p.texTL; parm.texBR = p.texBR; - parm.isWall = p.isWall; - if(p.isWall) + parm.isWall = isWall; + if(parm.isWall) { parm.wall.leftEdge = p.wall.leftEdge; parm.wall.rightEdge = p.wall.rightEdge; @@ -1209,7 +1219,7 @@ static bool renderWorldPoly(rvertex_t *rvertices, uint numVertices, } else { - RL_AddPolyWithCoordsModulationReflection(p.isWall? PT_TRIANGLE_STRIP : PT_FAN, + RL_AddPolyWithCoordsModulationReflection(isWall? PT_TRIANGLE_STRIP : PT_FAN, p.flags | (hasDynlights? RPF_HAS_DYNLIGHTS : 0), numVertices, rvertices, rcolors, primaryCoords, interCoords, @@ -1432,7 +1442,8 @@ static void writeWallSection(Segment &segment, int section, parm.flags = RPF_DEFAULT | (skyMasked? RPF_SKYMASK : 0); parm.forceOpaque = wallSpec.flags.testFlag(WallSpec::ForceOpaque); parm.alpha = parm.forceOpaque? 1 : opacity; - parm.bsuf = useBias? &segment.biasSurface(wallSpec.section) : 0; + parm.elem = &segment; + parm.subElemIndex = wallSpec.section; parm.texTL = &texQuad[0]; parm.texBR = &texQuad[1]; @@ -1448,7 +1459,6 @@ static void writeWallSection(Segment &segment, int section, parm.materialOrigin = &materialOrigin; parm.materialScale = &materialScale; - parm.isWall = true; parm.wall.sectionWidth = de::abs(Vector2d(rightEdge.origin() - leftEdge.origin()).length()); parm.wall.leftEdge = &leftEdge; parm.wall.rightEdge = &rightEdge; @@ -1675,8 +1685,8 @@ static void writeLeafPlane(Plane &plane) rendworldpoly_params_t parm; zap(parm); parm.flags = RPF_DEFAULT; - parm.isWall = false; - parm.bsuf = useBias? &leaf->biasSurface(plane.indexInSector()) : 0; + parm.elem = leaf; + parm.subElemIndex = plane.indexInSector(); parm.texTL = &texTL; parm.texBR = &texBR; parm.surfaceLightLevelDL = parm.surfaceLightLevelDR = 0; @@ -2575,7 +2585,7 @@ static void drawSource(BiasSource *s) Vector3d eye(vOrigin[VX], vOrigin[VZ], vOrigin[VY]); coord_t distToEye = (s->origin() - eye).length(); - drawStar(s->origin(), 25 + s->intensity() / 20, + drawStar(s->origin(), 25 + s->evaluateIntensity() / 20, Vector4f(s->color(), 1.0f / de::max(float((distToEye - 100) / 1000), 1.f))); drawLabel(s->origin(), labelForSource(s)); } diff --git a/doomsday/client/src/world/bspleaf.cpp b/doomsday/client/src/world/bspleaf.cpp index 5bd6d6bb32..e6b1edc08f 100644 --- a/doomsday/client/src/world/bspleaf.cpp +++ b/doomsday/client/src/world/bspleaf.cpp @@ -32,6 +32,7 @@ #ifdef __CLIENT__ # include "BiasSurface" +# include "world/map.h" #endif #include "world/bspleaf.h" @@ -281,6 +282,53 @@ DENG2_PIMPL(BspLeaf) #undef MIN_TRIANGLE_EPSILON } + /** + * @todo This could be enhanced so that only the lights on the right + * side of the surface are taken into consideration. + */ + void updateAffected(BiasSurface &bsuf, int group) + { + // If the data is already up to date, nothing needs to be done. + uint lastChangeFrame = self.map().biasLastChangeOnFrame(); + if(bsuf.lastUpdateOnFrame() == lastChangeFrame) + return; + + bsuf.setLastUpdateOnFrame(lastChangeFrame); + + bsuf.clearAffected(); + + Plane const &plane = sector->plane(group); + Surface const &surface = plane.surface(); + + Vector3d surfacePoint(poly->center(), plane.visHeight()); + + 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; + + bsuf.addAffected(source->evaluateIntensity() / de::max(distance, 1.0), source); + } + } + #endif // __CLIENT__ }; @@ -496,7 +544,7 @@ BiasSurface &BspLeaf::biasSurface(int group) return **foundAt; } - BiasSurface *bsuf = new BiasSurface(*this, group, numFanVertices()); + BiasSurface *bsuf = new BiasSurface(numFanVertices()); d->biasSurfaces.insert(group, bsuf); return *bsuf; } @@ -509,6 +557,21 @@ void BspLeaf::updateBiasAffection(BiasTracker &changes) } } +void BspLeaf::lightPoly(int group, int vertCount, rvertex_t const *positions, + ColorRawf *colors) +{ + BiasSurface &bsuf = biasSurface(group); + + // Should we update? + //if(devUpdateAffected) + { + d->updateAffected(bsuf, group); + } + + bsuf.lightPoly(d->sector->plane(group).surface().normal(), vertCount, + positions, colors); +} + ShadowLink *BspLeaf::firstShadowLink() const { return _shadows; diff --git a/doomsday/client/src/world/segment.cpp b/doomsday/client/src/world/segment.cpp index 4b9171c88e..cdcaeaf26b 100644 --- a/doomsday/client/src/world/segment.cpp +++ b/doomsday/client/src/world/segment.cpp @@ -31,6 +31,7 @@ #ifdef __CLIENT__ # include "BiasSurface" +# include "world/map.h" #endif #include "world/segment.h" @@ -86,6 +87,53 @@ DENG2_PIMPL(Segment) qDeleteAll(biasSurfaces); #endif } + +#ifdef __CLIENT__ + /** + * @todo This could be enhanced so that only the lights on the right + * side of the surface are taken into consideration. + */ + void updateAffected(BiasSurface &bsuf, int group) + { + DENG_UNUSED(group); + + // If the data is already up to date, nothing needs to be done. + uint lastChangeFrame = self.map().biasLastChangeOnFrame(); + if(bsuf.lastUpdateOnFrame() == lastChangeFrame) + return; + + bsuf.setLastUpdateOnFrame(lastChangeFrame); + + bsuf.clearAffected(); + + Surface const &surface = lineSide->middle(); + Vector2d const &from = self.from().origin(); + Vector2d const &to = self.to().origin(); + + 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() - self.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; + + bsuf.addAffected(source->evaluateIntensity() / de::max(distance, 1.0), source); + } + } +#endif }; Segment::Segment(Line::Side *lineSide, HEdge *hedge) @@ -204,7 +252,7 @@ BiasSurface &Segment::biasSurface(int group) return **foundAt; } - BiasSurface *bsuf = new BiasSurface(*this, group, 4); + BiasSurface *bsuf = new BiasSurface(4); d->biasSurfaces.insert(group, bsuf); return *bsuf; } @@ -217,6 +265,22 @@ void Segment::updateBiasAffection(BiasTracker &changes) } } +void Segment::lightPoly(int group, int vertCount, rvertex_t const *positions, + ColorRawf *colors) +{ + DENG_ASSERT(hasLineSide()); // sanity check + + BiasSurface &bsuf = biasSurface(group); + + // Should we update? + //if(devUpdateAffected) + { + d->updateAffected(bsuf, group); + } + + bsuf.lightPoly(d->lineSide->middle().normal(), vertCount, positions, colors); +} + #endif // __CLIENT__ coord_t Segment::pointDistance(const_pvec2d_t point, coord_t *offset) const