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