diff --git a/doomsday/client/client.pro b/doomsday/client/client.pro index b841073cac..69ab0850f0 100644 --- a/doomsday/client/client.pro +++ b/doomsday/client/client.pro @@ -309,9 +309,9 @@ DENG_HEADERS += \ include/render/skyfixedge.h \ include/render/surfacedecorator.h \ include/render/trianglestripbuilder.h \ + include/render/vectorlightdata.h \ include/render/viewports.h \ include/render/vissprite.h \ - include/render/vlight.h \ include/render/vr.h \ include/render/walledge.h \ include/render/wallspec.h \ @@ -640,7 +640,6 @@ SOURCES += \ src/render/trianglestripbuilder.cpp \ src/render/viewports.cpp \ src/render/vissprite.cpp \ - src/render/vlight.cpp \ src/render/vr.cpp \ src/render/walledge.cpp \ src/render/wallspec.cpp \ diff --git a/doomsday/client/include/de_render.h b/doomsday/client/include/de_render.h index 2077864469..f4f09347e8 100644 --- a/doomsday/client/include/de_render.h +++ b/doomsday/client/include/de_render.h @@ -38,7 +38,6 @@ #include "render/billboard.h" #include "render/cameralensfx.h" #include "render/vissprite.h" -#include "render/vlight.h" #endif #include "r_util.h" diff --git a/doomsday/client/include/render/projectedtexturedata.h b/doomsday/client/include/render/projectedtexturedata.h index e25bb6da9f..a3a541dce7 100644 --- a/doomsday/client/include/render/projectedtexturedata.h +++ b/doomsday/client/include/render/projectedtexturedata.h @@ -25,6 +25,7 @@ /** * POD for a texture => surface projection. + * @ingroup render */ struct ProjectedTextureData { diff --git a/doomsday/client/include/render/rend_main.h b/doomsday/client/include/render/rend_main.h index f380d303e9..6f075e0099 100644 --- a/doomsday/client/include/render/rend_main.h +++ b/doomsday/client/include/render/rend_main.h @@ -33,6 +33,7 @@ class Sector; class SectorCluster; +struct VectorLightData; namespace de { class Map; @@ -233,6 +234,20 @@ de::Vector3f Rend_LuminousColor(de::Vector3f const &color, float light); */ coord_t Rend_PlaneGlowHeight(float intensity); +/** + * @param point World space point to evaluate. + * @param ambientColor Ambient color of the object being lit. + * @param subspace Subspace in which @a origin resides. + * @param starkLight @c true= World light has a more pronounced affect. + * + * @todo Does not belong here. + */ +de::duint Rend_CollectAffectingLights(de::Vector3d const &point, + de::Vector3f const &ambientColor = de::Vector3f(1, 1, 1), ConvexSubspace *subspace = nullptr, + bool starkLight = false); + +void Rend_DrawVectorLight(VectorLightData const &vlight, dfloat alpha); + /** * Selects a Material for the given map @a surface considering the current map * renderer configuration. diff --git a/doomsday/client/include/render/rendersystem.h b/doomsday/client/include/render/rendersystem.h index e961bb5811..0a1dda9af1 100644 --- a/doomsday/client/include/render/rendersystem.h +++ b/doomsday/client/include/render/rendersystem.h @@ -27,6 +27,7 @@ #include "DrawLists" #include "settingsregister.h" #include "projectedtexturedata.h" +#include "vectorlightdata.h" class AngleClipper; class ModelRenderer; @@ -64,6 +65,7 @@ struct Store uint vertCount, vertMax; }; +/// @todo make private to RenderSystem struct ProjectionList { struct Node @@ -73,25 +75,45 @@ struct ProjectionList }; Node *head = nullptr; - bool sortByLuma; ///< @c true= Sort from brightest to darkest. + bool sortByLuma; ///< @c true= Sort from brightest to darkest. static void init(); - - static void reset(); + static void rewind(); inline ProjectionList &operator << (ProjectedTextureData &texp) { return add(texp); } ProjectionList &add(ProjectedTextureData &texp); private: - // Projection list nodes. static Node *firstNode; static Node *cursorNode; static Node *newNode(); +}; - /// Average color * alpha. - static dfloat luminosity(ProjectedTextureData const &texp); +/// @todo make private to RenderSystem +struct VectorLightList +{ + struct Node + { + Node *next, *nextUsed; + VectorLightData vlight; + }; + + Node *head = nullptr; + + static void init(); + static void rewind(); + + inline VectorLightList &operator << (VectorLightData &texp) { return add(texp); } + + VectorLightList &add(VectorLightData &texp); + +private: + static Node *firstNode; + static Node *cursorNode; + + static Node *newNode(); }; /** @@ -125,25 +147,30 @@ class RenderSystem : public de::System */ Store &buffer(); - void clearDrawLists(); - - void resetDrawLists(); - + /** + * Provides access to the DrawLists collection for conveniently writing geometry. + */ DrawLists &drawLists(); -public: // Texture => surface projection lists ----------------------------------- + /** + * To be called manually, to clear all persistent data held by/for the draw lists + * (e.g., during re-initialization). + * + * @todo Use a de::Observers based mechanism. + */ + void clearDrawLists(); /** - * To be called to initialize the projector when the current map changes. - * @todo make private + * @todo Use a de::Observers based mechanism. */ - void projectorInitForMap(de::Map &map); + void worldSystemMapChanged(de::Map &map); /** - * To be called at the start of a render frame to clear the projection lists - * to prepare for subsequent drawing. + * @todo Use a de::Observers based mechanism. */ - void projectorReset(); + void beginFrame(); + +public: // Texture => surface projection lists ----------------------------------- /** * Find/create a new projection list. @@ -152,7 +179,7 @@ class RenderSystem : public de::System * list index is non-zero return the associated list. Otherwise * allocate a new list and write it's index back to this address. * - * @param sortByLuma @c true= The list should maintain luma-sorted order. + * @param sortByLuma @c true= Maintain a luminosity sorted order (descending). * * @return ProjectionList associated with the (possibly newly attributed) index. */ @@ -166,6 +193,27 @@ class RenderSystem : public de::System */ de::LoopResult forAllSurfaceProjections(de::duint listIdx, std::function func) const; +public: // VectorLight affection lists ------------------------------------------- + + /** + * Find/create a new vector light list. + * + * @param listIdx Address holding the list index to retrieve. If the referenced + * list index is non-zero return the associated list. Otherwise + * allocate a new list and write it's index back to this address. + * + * @return VectorLightList associated with the (possibly newly attributed) index. + */ + VectorLightList &findVectorLightList(de::duint *listIdx); + + /** + * Iterate through the referenced vector light list. + * + * @param listIdx Unique identifier of the list to process. + * @param func Callback to make for each VectorLight. + */ + de::LoopResult forAllVectorLights(de::duint listIdx, std::function func); + public: /** * Register the console commands, variables, etc..., of this module. diff --git a/doomsday/client/include/render/vectorlightdata.h b/doomsday/client/include/render/vectorlightdata.h new file mode 100644 index 0000000000..ffa64c5eb3 --- /dev/null +++ b/doomsday/client/include/render/vectorlightdata.h @@ -0,0 +1,40 @@ +/** @file vectorlightdata.h Vector light source data. + * + * @authors Copyright © 2003-2014 Jaakko Keränen + * @authors Copyright © 2006-2015 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 CLIENT_RENDER_VECTORLIGHTDATA_H +#define CLIENT_RENDER_VECTORLIGHTDATA_H + +#include + +/** + * POD for a vector light source affection. + * @ingroup render + */ +struct VectorLightData +{ + de::dfloat approxDist; ///< Only an approximation. + de::Vector3f direction; ///< Normalized vector from light origin to illumination point. + de::Vector3f color; ///< How intense the light is (0..1, RGB). + de::dfloat offset; + de::dfloat lightSide; + de::dfloat darkSide; ///< Factors for world light. + bool affectedByAmbient; +}; + +#endif // CLIENT_RENDER_VECTORLIGHTDATA_H diff --git a/doomsday/client/include/render/vlight.h b/doomsday/client/include/render/vlight.h deleted file mode 100644 index 0a68016f52..0000000000 --- a/doomsday/client/include/render/vlight.h +++ /dev/null @@ -1,82 +0,0 @@ -/** @file vlight.h Vector light sources and source lists. - * - * @authors Copyright © 2003-2014 Jaakko Keränen - * @authors Copyright © 2006-2014 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, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#ifndef DENG_CLIENT_RENDER_VECTORLIGHT_H -#define DENG_CLIENT_RENDER_VECTORLIGHT_H - -#include -#include - -#include "world/map.h" - -class ConvexSubspace; - -/** - * Vector light source. - * @ingroup render - */ -struct VectorLight -{ - float approxDist; ///< Only an approximation. - de::Vector3f direction; ///< Normalized vector from light origin to illumination point. - de::Vector3f color; ///< How intense the light is (0..1, RGB). - float offset; - float lightSide, darkSide; ///< Factors for world light. - bool affectedByAmbient; -}; - -/** - * Initialize the vlight system in preparation for rendering view(s) of the - * game world. - */ -void VL_InitForMap(de::Map &map); - -/** - * Moves all used vlight nodes to the list of unused nodes, so they can be - * reused. - */ -void VL_InitForNewFrame(); - -struct collectaffectinglights_params_t -{ - de::Vector3d origin; - de::Vector3f ambientColor; - ConvexSubspace *subspace; - bool starkLight; ///< World light has a more pronounced effect. -}; - -uint R_CollectAffectingLights(collectaffectinglights_params_t const *params); - -/** - * Calls func for all vlights in the given list. - * - * @param listIdx Unique identifier of the list to process. - * @param callback Callback to make for each visited projection. - * @param context Passed to the callback. - * - * @return @c 0 iff iteration completed wholly. - */ -int VL_ListIterator(uint listIdx, - std::function callback, - void *context = 0); - -void Rend_DrawVectorLight(VectorLight const *vlight, float alpha); - -#endif // DENG_CLIENT_RENDER_VECTORLIGHT_H diff --git a/doomsday/client/include/world/mapobject.h b/doomsday/client/include/world/mapobject.h index b6b11283c8..0efab00efb 100644 --- a/doomsday/client/include/world/mapobject.h +++ b/doomsday/client/include/world/mapobject.h @@ -1,7 +1,7 @@ /** @file mapobject.h Base class for all world map objects. * * @authors Copyright © 2013 Jaakko Keränen - * @authors Copyright © 2013 Daniel Swanson + * @authors Copyright © 2013-2015 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -73,6 +73,10 @@ class MapObject */ de::Vector3d const &origin() const; + inline de::ddouble x() const { return origin().x; } + inline de::ddouble y() const { return origin().y; } + inline de::ddouble z() const { return origin().z; } + /** * Change the origin of the object in map space. * @@ -119,7 +123,7 @@ class MapObject * * @see setIndexInMap() */ - int indexInMap() const; + de::dint indexInMap() const; /** * Change the "in-map" index attributed to the map object. @@ -129,12 +133,12 @@ class MapObject * * @see indexInMap() */ - void setIndexInMap(int newIndex = NoIndex); + void setIndexInMap(de::dint newIndex = NoIndex); private: DENG2_PRIVATE(d) }; -} // namespace de +} // namespace de -#endif // DENG_WORLD_MAPOBJECT_H +#endif // DENG_WORLD_MAPOBJECT_H diff --git a/doomsday/client/src/render/billboard.cpp b/doomsday/client/src/render/billboard.cpp index f4088e7049..5cc14a8b77 100644 --- a/doomsday/client/src/render/billboard.cpp +++ b/doomsday/client/src/render/billboard.cpp @@ -31,7 +31,6 @@ #include "r_util.h" #include "render/rend_main.h" #include "render/vissprite.h" -#include "render/vlight.h" #include "MaterialVariantSpec" #include "Texture" @@ -54,6 +53,11 @@ dint noSpriteZWrite; dbyte devNoSprites; +static inline RenderSystem &rendSys() +{ + return ClientApp::renderSystem(); +} + static inline ResourceSystem &resSys() { return ClientApp::resourceSystem(); @@ -243,67 +247,44 @@ static void applyUniformColor(dint count, dgl_color_t *colors, dfloat const *rgb } } -struct lightspriteworker_params_t -{ - Vector3f color, extra; - Vector3f normal; - duint numProcessed, max; -}; - -static void lightSprite(VectorLight const &vlight, lightspriteworker_params_t &parms) -{ - dfloat strength = vlight.direction.dot(parms.normal) - + vlight.offset; // Shift toward the light a little. - - // Ability to both light and shade. - if(strength > 0) - { - strength *= vlight.lightSide; - } - else - { - strength *= vlight.darkSide; - } - - Vector3f &dest = vlight.affectedByAmbient? parms.color : parms.extra; - dest += vlight.color * de::clamp(-1.f, strength, 1.f); -} - -static dint lightSpriteWorker(VectorLight const *vlight, void *context) -{ - auto &parms = *static_cast(context); - - lightSprite(*vlight, parms); - parms.numProcessed += 1; - - // Time to stop? - return parms.max && parms.numProcessed == parms.max; -} - /** * Calculate vertex lighting. */ -static void Spr_VertexColors(dint count, dgl_color_t *out, dgl_vertex_t *normal, - duint vLightListIdx, duint maxLights, dfloat const *ambient) +static void Spr_VertexColors(dint count, dgl_color_t *out, dgl_vertex_t *normalIt, + duint lightListIdx, duint maxLights, dfloat const *ambient) { - DENG2_ASSERT(out && normal); + DENG2_ASSERT(out && normalIt); Vector3f const saturated(1, 1, 1); - lightspriteworker_params_t parms; - for(dint i = 0; i < count; ++i, out++, normal++) + for(dint i = 0; i < count; ++i, out++, normalIt++) { - // Begin with total darkness. - parms.color = Vector3f(); - parms.extra = Vector3f(); - parms.normal = Vector3f(normal->xyz); - parms.max = maxLights; - parms.numProcessed = 0; + Vector3f const normal(normalIt->xyz); + + // Accumulate contributions from all affecting lights. + Vector3f accum[2]; // Begin with total darkness [color, extra]. + dint numProcessed = 0; + rendSys().forAllVectorLights(lightListIdx, [&maxLights, &normal + , &accum, &numProcessed] (VectorLightData const &vlight) + { + numProcessed += 1; + + dfloat strength = vlight.direction.dot(normal) + + vlight.offset; // Shift toward the light a little. + + // Ability to both light and shade. + if(strength > 0) strength *= vlight.lightSide; + else strength *= vlight.darkSide; + + accum[vlight.affectedByAmbient? 0 : 1] + += vlight.color * de::clamp(-1.f, strength, 1.f); - VL_ListIterator(vLightListIdx, lightSpriteWorker, &parms); + // Time to stop? + return (maxLights && numProcessed == maxLights); + }); // Check for ambient and convert to ubyte. - Vector3f color = (parms.color.max(ambient) + parms.extra).min(saturated); + Vector3f color = (accum[0].max(ambient) + accum[1]).min(saturated); out->rgba[0] = dbyte( 255 * color.x ); out->rgba[1] = dbyte( 255 * color.y ); @@ -415,16 +396,6 @@ MaterialVariantSpec const &Rend_SpriteMaterialSpec(dint tclass, dint tmap) 1, -2, -1, true, true, true, false); } -static dint drawVectorLightWorker(VectorLight const *vlight, void *context) -{ - auto const distFromViewer = *static_cast(context); - if(distFromViewer < 1600 - 8) - { - Rend_DrawVectorLight(vlight, 1 - distFromViewer / 1600); - } - return false; // Continue iteration. -} - void Rend_DrawSprite(vissprite_t const &spr) { drawspriteparams_t const &parm = *VS_SPRITE(&spr); @@ -647,8 +618,15 @@ void Rend_DrawSprite(vissprite_t const &spr) glTranslatef(spr.pose.origin[0], spr.pose.origin[2], spr.pose.origin[1]); - coord_t distFromViewer = de::abs(spr.pose.distance); - VL_ListIterator(spr.light.vLightListIdx, drawVectorLightWorker, &distFromViewer); + coord_t const distFromViewer = de::abs(spr.pose.distance); + rendSys().forAllVectorLights(spr.light.vLightListIdx, [&distFromViewer] (VectorLightData const &vlight) + { + if(distFromViewer < 1600 - 8) + { + Rend_DrawVectorLight(vlight, 1 - distFromViewer / 1600); + } + return LoopContinue; + }); glMatrixMode(GL_MODELVIEW); glPopMatrix(); diff --git a/doomsday/client/src/render/r_main.cpp b/doomsday/client/src/render/r_main.cpp index 4ebd4634eb..7ed85cf7e7 100644 --- a/doomsday/client/src/render/r_main.cpp +++ b/doomsday/client/src/render/r_main.cpp @@ -27,7 +27,6 @@ #include "render/billboard.h" #include "render/rend_main.h" #include "render/vissprite.h" -#include "render/vlight.h" #include "world/map.h" #include "world/p_players.h" @@ -40,19 +39,24 @@ using namespace de; -int levelFullBright; -int weaponOffsetScaleY = 1000; -int psp3d; +dint levelFullBright; +dint weaponOffsetScaleY = 1000; +dint psp3d; -float pspLightLevelMultiplier = 1; -float pspOffset[2]; +dfloat pspLightLevelMultiplier = 1; +dfloat pspOffset[2]; /* * Console variables: */ -float weaponFOVShift = 45; -float weaponOffsetScale = 0.3183f; // 1/Pi -byte weaponScaleMode = SCALEMODE_SMART_STRETCH; +dfloat weaponFOVShift = 45; +dfloat weaponOffsetScale = 0.3183f; // 1/Pi +dbyte weaponScaleMode = SCALEMODE_SMART_STRETCH; + +static inline RenderSystem &rendSys() +{ + return ClientApp::renderSystem(); +} static inline ResourceSystem &resSys() { @@ -68,13 +72,13 @@ static MaterialVariantSpec const &pspriteMaterialSpec() static void setupPSpriteParams(rendpspriteparams_t *params, vispsprite_t *spr) { -#define WEAPONTOP 32 /// @todo Currently hardcoded here and in the plugins. + static dint const WEAPONTOP = 32; /// @todo Currently hardcoded here and in the plugins. - ddpsprite_t *psp = spr->psp; - float const offScaleY = weaponOffsetScaleY / 1000.0f; - int const spriteIdx = psp->statePtr->sprite; - int const frameIdx = psp->statePtr->frame; - Sprite const &sprite = resSys().sprite(spriteIdx, frameIdx); + ddpsprite_t *psp = spr->psp; + dfloat const offScaleY = weaponOffsetScaleY / 1000.0f; + dint const spriteIdx = psp->statePtr->sprite; + dint const frameIdx = psp->statePtr->frame; + Sprite const &sprite = resSys().sprite(spriteIdx, frameIdx); SpriteViewAngle const &sprViewAngle = sprite.viewAngle(0); MaterialAnimator &matAnimator = sprViewAngle.material->getAnimator(pspriteMaterialSpec()); @@ -88,9 +92,9 @@ static void setupPSpriteParams(rendpspriteparams_t *params, vispsprite_t *spr) variantspecification_t const &texSpec = tex.spec().variant; - params->pos[VX] = psp->pos[VX] + texOrigin.x + pspOffset[VX] - texSpec.border; - params->pos[VY] = WEAPONTOP + offScaleY * (psp->pos[VY] - WEAPONTOP) + texOrigin.y + - pspOffset[VY] - texSpec.border; + params->pos[0] = psp->pos[0] + texOrigin.x + pspOffset[0] - texSpec.border; + params->pos[1] = WEAPONTOP + offScaleY * (psp->pos[1] - WEAPONTOP) + texOrigin.y + + pspOffset[1] - texSpec.border; params->width = matDimensions.x + texSpec.border * 2; params->height = matDimensions.y + texSpec.border * 2; @@ -120,7 +124,7 @@ static void setupPSpriteParams(rendpspriteparams_t *params, vispsprite_t *spr) Vector4f color = map.lightGrid().evaluate(spr->origin); // Apply light range compression. - for(int i = 0; i < 3; ++i) + for(dint i = 0; i < 3; ++i) { color[i] += Rend_LightAdaptationDelta(color[i]); } @@ -132,7 +136,7 @@ static void setupPSpriteParams(rendpspriteparams_t *params, vispsprite_t *spr) Vector4f const color = spr->data.sprite.bspLeaf->subspace().cluster().lightSourceColorfIntensity(); // No need for distance attentuation. - float lightLevel = color.w; + dfloat lightLevel = color.w; // Add extra light plus bonus. lightLevel += Rend_ExtraLightDelta(); @@ -143,23 +147,17 @@ static void setupPSpriteParams(rendpspriteparams_t *params, vispsprite_t *spr) Rend_ApplyLightAdaptation(lightLevel); // Determine the final ambientColor. - for(int i = 0; i < 3; ++i) + for(dint i = 0; i < 3; ++i) { params->ambientColor[i] = lightLevel * color[i]; } } - Rend_ApplyTorchLight(params->ambientColor, 0); - collectaffectinglights_params_t lparams; zap(lparams); - lparams.origin = Vector3d(spr->origin); - lparams.subspace = spr->data.sprite.bspLeaf->subspacePtr(); - lparams.ambientColor = Vector3f(params->ambientColor); - - params->vLightListIdx = R_CollectAffectingLights(&lparams); + params->vLightListIdx = + Rend_CollectAffectingLights(Vector3d(spr->origin), Vector3f(params->ambientColor), + spr->data.sprite.bspLeaf->subspacePtr()); } - -#undef WEAPONTOP } void Rend_Draw2DPlayerSprites() @@ -212,12 +210,12 @@ static void setupModelParamsForVisPSprite(vissprite_t &vis, vispsprite_t const * params->id = spr->data.model.id; params->selector = spr->data.model.selector; params->flags = spr->data.model.flags; - vis.pose.origin[VX] = spr->origin[VX]; - vis.pose.origin[VY] = spr->origin[VY]; - vis.pose.origin[VZ] = spr->origin[VZ]; - vis.pose.srvo[VX] = spr->data.model.visOff[VX]; - vis.pose.srvo[VY] = spr->data.model.visOff[VY]; - vis.pose.srvo[VZ] = spr->data.model.visOff[VZ] - spr->data.model.floorClip; + vis.pose.origin[0] = spr->origin[0]; + vis.pose.origin[1] = spr->origin[1]; + vis.pose.origin[2] = spr->origin[2]; + vis.pose.srvo[0] = spr->data.model.visOff[0]; + vis.pose.srvo[1] = spr->data.model.visOff[1]; + vis.pose.srvo[2] = spr->data.model.visOff[2] - spr->data.model.floorClip; vis.pose.topZ = spr->data.model.topZ; vis.pose.distance = -10; vis.pose.yaw = spr->data.model.yaw; @@ -233,12 +231,12 @@ static void setupModelParamsForVisPSprite(vissprite_t &vis, vispsprite_t const * params->shinePitchOffset = vpitch + 90; params->shineTranslateWithViewerPos = false; params->shinepspriteCoordSpace = true; - vis.light.ambientColor[CA] = spr->data.model.alpha; + vis.light.ambientColor[3] = spr->data.model.alpha; if((levelFullBright || spr->data.model.stateFullBright) && !spr->data.model.mf->testSubFlag(0, MFF_DIM)) { - vis.light.ambientColor[CR] = vis.light.ambientColor[CG] = vis.light.ambientColor[CB] = 1; + vis.light.ambientColor[0] = vis.light.ambientColor[1] = vis.light.ambientColor[2] = 1; vis.light.vLightListIdx = 0; } else @@ -249,7 +247,7 @@ static void setupModelParamsForVisPSprite(vissprite_t &vis, vispsprite_t const * { Vector4f color = map.lightGrid().evaluate(vis.pose.origin); // Apply light range compression. - for(int i = 0; i < 3; ++i) + for(dint i = 0; i < 3; ++i) { color[i] += Rend_LightAdaptationDelta(color[i]); } @@ -262,7 +260,7 @@ static void setupModelParamsForVisPSprite(vissprite_t &vis, vispsprite_t const * Vector4f const color = spr->data.model.bspLeaf->subspace().cluster().lightSourceColorfIntensity(); // No need for distance attentuation. - float lightLevel = color.w; + dfloat lightLevel = color.w; // Add extra light. lightLevel += Rend_ExtraLightDelta(); @@ -272,21 +270,17 @@ static void setupModelParamsForVisPSprite(vissprite_t &vis, vispsprite_t const * Rend_ApplyLightAdaptation(lightLevel); // Determine the final ambientColor. - for(int i = 0; i < 3; ++i) + for(dint i = 0; i < 3; ++i) { vis.light.ambientColor[i] = lightLevel * color[i]; } } - Rend_ApplyTorchLight(vis.light.ambientColor, vis.pose.distance); - collectaffectinglights_params_t lparams; zap(lparams); - lparams.origin = Vector3d(spr->origin); - lparams.subspace = spr->data.model.bspLeaf->subspacePtr(); - lparams.ambientColor = Vector3f(vis.light.ambientColor); - lparams.starkLight = true; - - vis.light.vLightListIdx = R_CollectAffectingLights(&lparams); + vis.light.vLightListIdx = + Rend_CollectAffectingLights(Vector3d(spr->origin), vis.light.ambientColor, + spr->data.model.bspLeaf->subspacePtr(), + true /*stark world light*/); } } diff --git a/doomsday/client/src/render/r_things.cpp b/doomsday/client/src/render/r_things.cpp index a404e76fb8..4e4cf0c898 100644 --- a/doomsday/client/src/render/r_things.cpp +++ b/doomsday/client/src/render/r_things.cpp @@ -57,7 +57,7 @@ static inline RenderSystem &rendSys() } static void evaluateLighting(Vector3d const &origin, ConvexSubspace &subspaceAtOrigin, - coord_t distToEye, bool fullbright, Vector4f &ambientColor, uint *vLightListIdx) + coord_t distToEye, bool fullbright, Vector4f &ambientColor, duint *vLightListIdx) { if(fullbright) { @@ -74,7 +74,7 @@ static void evaluateLighting(Vector3d const &origin, ConvexSubspace &subspaceAtO // Evaluate the position in the light grid. Vector4f color = map.lightGrid().evaluate(origin); // Apply light range compression. - for(int i = 0; i < 3; ++i) + for(dint i = 0; i < 3; ++i) { color[i] += Rend_LightAdaptationDelta(color[i]); } @@ -84,7 +84,7 @@ static void evaluateLighting(Vector3d const &origin, ConvexSubspace &subspaceAtO { Vector4f const color = cluster.lightSourceColorfIntensity(); - float lightLevel = color.w; + dfloat lightLevel = color.w; /* if(spr->type == VSPR_DECORATION) { // Wall decorations receive an additional light delta. @@ -102,35 +102,25 @@ static void evaluateLighting(Vector3d const &origin, ConvexSubspace &subspaceAtO // Determine the final color. ambientColor = color * lightLevel; } - Rend_ApplyTorchLight(ambientColor, distToEye); - collectaffectinglights_params_t parm; zap(parm); - parm.origin[VX] = origin.x; - parm.origin[VY] = origin.y; - parm.origin[VZ] = origin.z; - parm.subspace = &subspaceAtOrigin; - parm.ambientColor[0] = ambientColor.x; - parm.ambientColor[1] = ambientColor.y; - parm.ambientColor[2] = ambientColor.z; - - *vLightListIdx = R_CollectAffectingLights(&parm); + *vLightListIdx = Rend_CollectAffectingLights(origin, ambientColor, &subspaceAtOrigin); } } /// @todo use Mobj_OriginSmoothed -static Vector3d mobjOriginSmoothed(mobj_t *mo) +static Vector3d mobjOriginSmoothed(mobj_t *mob) { - DENG_ASSERT(mo != 0); - coord_t moPos[] = { mo->origin[VX], mo->origin[VY], mo->origin[VZ] }; + DENG2_ASSERT(mob); + coord_t origin[] = { mob->origin[0], mob->origin[1], mob->origin[2] }; // The client may have a Smoother for this object. - if(isClient && mo->dPlayer && P_GetDDPlayerIdx(mo->dPlayer) != consolePlayer) + if(isClient && mob->dPlayer && P_GetDDPlayerIdx(mob->dPlayer) != consolePlayer) { - Smoother_Evaluate(clients[P_GetDDPlayerIdx(mo->dPlayer)].smoother, moPos); + Smoother_Evaluate(clients[P_GetDDPlayerIdx(mob->dPlayer)].smoother, origin); } - return moPos; + return origin; } /** diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index fa2888714e..4365fcd3fa 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -2264,6 +2264,145 @@ static void projectDynamics(Surface const &surface, dfloat glowStrength, } } +/** + * World light can both light and shade. Normal objects get more shade than light + * (preventing them from ending up too bright compared to the ambient light). + */ +static bool lightWithWorldLight(Vector3d const & /*point*/, Vector3f const &ambientColor, + bool starkLight, VectorLightData &vlight) +{ + static Vector3f const worldLight(-.400891f, -.200445f, .601336f); + + de::zap(vlight); + vlight.direction = worldLight; + vlight.color = ambientColor; + vlight.affectedByAmbient = false; + vlight.approxDist = 0; + if(starkLight) + { + vlight.lightSide = .35f; + vlight.darkSide = .5f; + vlight.offset = 0; + } + else + { + vlight.lightSide = .2f; + vlight.darkSide = .8f; + vlight.offset = .3f; + } + return true; +} + +static bool lightWithLumobj(Vector3d const &point, Lumobj const &lum, VectorLightData &vlight) +{ + Vector3d const lumCenter(lum.x(), lum.y(), lum.z() + lum.zOffset()); + + // Is this light close enough? + ddouble const dist = M_ApproxDistance(M_ApproxDistance(lumCenter.x - point.x, + lumCenter.y - point.y), + point.z - lumCenter.z); + dfloat intensity = 0; + if(dist < Lumobj::radiusMax()) + { + intensity = de::clamp(0.f, dfloat(1 - dist / lum.radius()) * 2, 1.f); + } + if(intensity < .05f) return false; + + de::zap(vlight); + vlight.direction = (lumCenter - point) / dist; + vlight.color = lum.color() * intensity; + vlight.affectedByAmbient = true; + vlight.approxDist = dist; + vlight.lightSide = 1; + vlight.darkSide = 0; + vlight.offset = 0; + return true; +} + +static bool lightWithPlaneGlow(Vector3d const &point, SectorCluster const &cluster, + dint visPlaneIndex, VectorLightData &vlight) +{ + Plane const &plane = cluster.visPlane(visPlaneIndex); + Surface const &surface = plane.surface(); + + // Glowing at this moment? + Vector3f glowColor; + dfloat intensity = surface.glow(glowColor); + if(intensity < .05f) return false; + + coord_t const glowHeight = Rend_PlaneGlowHeight(intensity); + if(glowHeight < 2) return false; // Not too small! + + // In front of the plane? + Vector3d const pointOnPlane(cluster.center(), plane.heightSmoothed()); + ddouble const dist = (point - pointOnPlane).dot(surface.normal()); + if(dist < 0) return false; + + intensity *= 1 - dist / glowHeight; + if(intensity < .05f) return false; + + Vector3f const color = Rend_LuminousColor(glowColor, intensity); + if(color == Vector3f()) return false; + + de::zap(vlight); + vlight.direction = Vector3f(surface.normal().x, surface.normal().y, -surface.normal().z); + vlight.color = color; + vlight.affectedByAmbient = true; + vlight.approxDist = dist; + vlight.lightSide = 1; + vlight.darkSide = 0; + vlight.offset = 0.3f; + return true; +} + +duint Rend_CollectAffectingLights(Vector3d const &point, Vector3f const &ambientColor, + ConvexSubspace *subspace, bool starkLight) +{ + duint lightListIdx = 0; + + // Always apply an ambient world light. + { + VectorLightData vlight; + if(lightWithWorldLight(point, ambientColor, starkLight, vlight)) + { + rendSys().findVectorLightList(&lightListIdx) + << vlight; // a copy is made. + } + } + + // Add extra light by interpreting nearby sources. + if(subspace) + { + // Interpret lighting from luminous-objects near the origin and which + // are in contact the specified subspace and add them to the identified list. + R_ForAllSubspaceLumContacts(*subspace, [&point, &lightListIdx] (Lumobj &lum) + { + VectorLightData vlight; + if(lightWithLumobj(point, lum, vlight)) + { + rendSys().findVectorLightList(&lightListIdx) + << vlight; // a copy is made. + } + return LoopContinue; + }); + + // Interpret vlights from glowing planes at the origin in the specfified + // subspace and add them to the identified list. + SectorCluster const &cluster = subspace->cluster(); + for(dint i = 0; i < cluster.sector().planeCount(); ++i) + { + VectorLightData vlight; + if(lightWithPlaneGlow(point, cluster, i, vlight)) + { + rendSys().findVectorLightList(&lightListIdx) + << vlight; // a copy is made. + } + } + } + + return lightListIdx; +} + /** * Fade the specified @a opacity value to fully transparent the closer the view * player is to the geometry. @@ -4350,17 +4489,7 @@ void Rend_RenderMap(Map &map) if(!freezeRLs) { // Prepare for rendering. - AngleClipper &clipper = rendSys().angleClipper(); - - rendSys().resetDrawLists(); // Clear the lists for new geometry. - - clipper.clearRanges(); // Clear the clipper. - - // Recycle the vlight lists. Currently done here as the lists are - // not shared by all viewports. - VL_InitForNewFrame(); - - R_BeginFrame(); + rendSys().beginFrame(); // Make vissprites of all the visible decorations. generateDecorationFlares(map); @@ -4371,6 +4500,8 @@ void Rend_RenderMap(Map &map) // Add the backside clipping range (if vpitch allows). if(vpitch <= 90 - yfov / 2 && vpitch >= -90 + yfov / 2) { + AngleClipper &clipper = rendSys().angleClipper(); + dfloat a = de::abs(vpitch) / (90 - yfov / 2); binangle_t startAngle = binangle_t(BANG_45 * Rend_FieldOfView() / 90) * (1 + a); binangle_t angLen = BANG_180 - startAngle; @@ -5302,6 +5433,19 @@ static void drawSoundEmitters(Map &map) } } +void Rend_DrawVectorLight(VectorLightData const &vlight, dfloat alpha) +{ + if(alpha < .0001f) return; + + dfloat const unitLength = 100; + glBegin(GL_LINES); + glColor4f(vlight.color.x, vlight.color.y, vlight.color.z, alpha); + glVertex3f(unitLength * vlight.direction.x, unitLength * vlight.direction.z, unitLength * vlight.direction.y); + glColor4f(vlight.color.x, vlight.color.y, vlight.color.z, 0); + glVertex3f(0, 0, 0); + glEnd(); +} + static String labelForGenerator(Generator const *gen) { DENG2_ASSERT(gen); diff --git a/doomsday/client/src/render/rend_model.cpp b/doomsday/client/src/render/rend_model.cpp index 9338db922e..b3e123a663 100644 --- a/doomsday/client/src/render/rend_model.cpp +++ b/doomsday/client/src/render/rend_model.cpp @@ -32,7 +32,6 @@ #include "world/clientmobjthinkerdata.h" #include "render/rend_model.h" #include "render/rend_main.h" -#include "render/vlight.h" #include "render/vissprite.h" #include "render/modelrenderer.h" #include "gl/gl_main.h" @@ -107,6 +106,11 @@ static uint vertexBufferSize; ///< Current number of vertices supported by the r static bool announcedVertexBufferMaxBreach; ///< @c true if an attempt has been made to expand beyond our capability. #endif +static inline RenderSystem &rendSys() +{ + return ClientApp::renderSystem(); +} + static inline ResourceSystem &resSys() { return ClientApp::resourceSystem(); @@ -450,95 +454,86 @@ static void Mod_LerpVertices(float inter, int count, ModelFrame const &from, } } -static void Mod_MirrorCoords(int count, Vector3f *coords, int axis) +static void Mod_MirrorCoords(dint count, Vector3f *coords, dint axis) { + DENG2_ASSERT(coords); for(; count-- > 0; coords++) { (*coords)[axis] = -(*coords)[axis]; } } -struct lightmodelvertexworker_params_t -{ - Vector3f color, extra; - float rotateYaw, rotatePitch; - Vector3f normal; - uint numProcessed, max; - bool invert; -}; - -static void lightModelVertex(VectorLight const &vlight, lightmodelvertexworker_params_t &parms) +/** + * Rotate a VectorLight direction vector from world space to model space. + * + * @param vlight Light to process. + * @param yaw Yaw rotation angle. + * @param pitch Pitch rotation angle. + * @param invert @c true= flip light normal (for use with inverted models). + * + * @todo Construct a rotation matrix once and use it for all lights. + */ +static Vector3f rotateLightVector(VectorLightData const &vlight, dfloat yaw, dfloat pitch, + bool invert = false) { - // We must transform the light vector to model space. - float vlightDirection[3] = { vlight.direction.x, vlight.direction.y, vlight.direction.z }; - M_RotateVector(vlightDirection, parms.rotateYaw, parms.rotatePitch); + dfloat rotated[3]; vlight.direction.decompose(rotated); + M_RotateVector(rotated, yaw, pitch); // Quick hack: Flip light normal if model inverted. - if(parms.invert) + if(invert) { - vlightDirection[VX] = -vlightDirection[VX]; - vlightDirection[VY] = -vlightDirection[VY]; + rotated[0] = -rotated[0]; + rotated[1] = -rotated[1]; } - float strength = Vector3f(vlightDirection).dot(parms.normal) - + vlight.offset; // Shift a bit towards the light. - - // Ability to both light and shade. - if(strength > 0) - { - strength *= vlight.lightSide; - } - else - { - strength *= vlight.darkSide; - } - - Vector3f &dest = vlight.affectedByAmbient? parms.color : parms.extra; - dest += vlight.color * de::clamp(-1.f, strength, 1.f); -} - -static int lightModelVertexWorker(VectorLight const *vlight, void *context) -{ - lightmodelvertexworker_params_t &parms = *static_cast(context); - - lightModelVertex(*vlight, parms); - parms.numProcessed += 1; - - // Time to stop? - return parms.max && parms.numProcessed == parms.max; + return Vector3f(rotated); } /** * Calculate vertex lighting. - * @todo construct a rotation matrix once and use it for all vertices. */ -static void Mod_VertexColors(Vector4ub *out, int count, Vector3f const *normCoords, - uint vLightListIdx, uint maxLights, Vector4f const &ambient, bool invert, - float rotateYaw, float rotatePitch) +static void Mod_VertexColors(Vector4ub *out, dint count, Vector3f const *normCoords, + duint lightListIdx, duint maxLights, Vector4f const &ambient, bool invert, + dfloat rotateYaw, dfloat rotatePitch) { Vector4f const saturated(1, 1, 1, 1); - lightmodelvertexworker_params_t parms; - for(int i = 0; i < count; ++i, out++, normCoords++) + for(dint i = 0; i < count; ++i, out++, normCoords++) { if(activeLod && !activeLod->hasVertex(i)) continue; - // Begin with total darkness. - parms.color = Vector3f(); - parms.extra = Vector3f(); - parms.normal = *normCoords; - parms.invert = invert; - parms.rotateYaw = rotateYaw; - parms.rotatePitch = rotatePitch; - parms.max = maxLights; - parms.numProcessed = 0; + Vector3f const &normal = *normCoords; + + // Accumulate contributions from all affecting lights. + dint numProcessed = 0; + Vector3f accum[2]; // Begin with total darkness [color, extra]. + rendSys().forAllVectorLights(lightListIdx, [&maxLights, &invert, &rotateYaw + , &rotatePitch, &normal + , &accum, &numProcessed] (VectorLightData const &vlight) + { + numProcessed += 1; + + // We must transform the light vector to model space. + Vector3f const lightDirection + = rotateLightVector(vlight, rotateYaw, rotatePitch, invert); + + dfloat strength = lightDirection.dot(normal) + + vlight.offset; // Shift a bit towards the light. + + // Ability to both light and shade. + if(strength > 0) strength *= vlight.lightSide; + else strength *= vlight.darkSide; - // Add light from each source. - VL_ListIterator(vLightListIdx, lightModelVertexWorker, &parms); + accum[vlight.affectedByAmbient? 0 : 1] + += vlight.color * de::clamp(-1.f, strength, 1.f); + + // Time to stop? + return (maxLights && numProcessed == maxLights); + }); // Check for ambient and convert to ubyte. - Vector4f color(parms.color.max(ambient) + parms.extra, ambient[3]); + Vector4f color(accum[0].max(ambient) + accum[1], ambient[3]); *out = (color.min(saturated) * 255).toVector4ub(); } @@ -547,9 +542,9 @@ static void Mod_VertexColors(Vector4ub *out, int count, Vector3f const *normCoor /** * Set all the colors in the array to bright white. */ -static void Mod_FullBrightVertexColors(int count, Vector4ub *colorCoords, float alpha) +static void Mod_FullBrightVertexColors(dint count, Vector4ub *colorCoords, dfloat alpha) { - DENG2_ASSERT(colorCoords != 0); + DENG2_ASSERT(colorCoords); for(; count-- > 0; colorCoords++) { *colorCoords = Vector4ub(255, 255, 255, 255 * alpha); @@ -559,9 +554,9 @@ static void Mod_FullBrightVertexColors(int count, Vector4ub *colorCoords, float /** * Set all the colors into the array to the same values. */ -static void Mod_FixedVertexColors(int count, Vector4ub *colorCoords, Vector4ub const &color) +static void Mod_FixedVertexColors(dint count, Vector4ub *colorCoords, Vector4ub const &color) { - DENG2_ASSERT(colorCoords != 0); + DENG2_ASSERT(colorCoords); for(; count-- > 0; colorCoords++) { *colorCoords = color; @@ -1066,16 +1061,6 @@ static void drawSubmodel(uint number, vissprite_t const &spr) GL_BlendMode(BM_NORMAL); } -static int drawLightVectorWorker(VectorLight const *vlight, void *context) -{ - coord_t distFromViewer = *static_cast(context); - if(distFromViewer < 1600 - 8) - { - Rend_DrawVectorLight(vlight, 1 - distFromViewer / 1600); - } - return false; // Continue iteration. -} - void Rend_DrawModel(vissprite_t const &spr) { drawmodelparams_t const &parm = *VS_MODEL(&spr); @@ -1117,10 +1102,17 @@ void Rend_DrawModel(vissprite_t const &spr) glMatrixMode(GL_MODELVIEW); glPushMatrix(); - glTranslatef(spr.pose.origin[VX], spr.pose.origin[VZ], spr.pose.origin[VY]); + glTranslatef(spr.pose.origin[0], spr.pose.origin[2], spr.pose.origin[1]); - coord_t distFromViewer = de::abs(spr.pose.distance); - VL_ListIterator(spr.light.vLightListIdx, drawLightVectorWorker, &distFromViewer); + coord_t const distFromViewer = de::abs(spr.pose.distance); + rendSys().forAllVectorLights(spr.light.vLightListIdx, [&distFromViewer] (VectorLightData const &vlight) + { + if(distFromViewer < 1600 - 8) + { + Rend_DrawVectorLight(vlight, 1 - distFromViewer / 1600); + } + return LoopContinue; + }); glMatrixMode(GL_MODELVIEW); glPopMatrix(); @@ -1176,11 +1168,12 @@ void Rend_DrawModel2(vissprite_t const &spr) // Ambient color and lighting vectors. rend.setAmbientLight(spr.light.ambientColor * .8f); rend.clearLights(); - VL_ListIterator(spr.light.vLightListIdx, [&rend] (VectorLight const *light, void *) -> int { + rendSys().forAllVectorLights(spr.light.vLightListIdx, [&rend] (VectorLightData const &vlight) + { // Use this when drawing the model. - rend.addLight(light->direction.xzy(), light->color); - return false; - }, nullptr); + rend.addLight(vlight.direction.xzy(), vlight.color); + return LoopContinue; + }); // Draw the model using the current animation state. p.model->draw(p.animator); diff --git a/doomsday/client/src/render/rend_particle.cpp b/doomsday/client/src/render/rend_particle.cpp index 144ea0a817..9d19591b08 100644 --- a/doomsday/client/src/render/rend_particle.cpp +++ b/doomsday/client/src/render/rend_particle.cpp @@ -43,7 +43,6 @@ #include "render/viewports.h" #include "render/rend_main.h" #include "render/rend_model.h" -#include "render/vlight.h" #include "render/vissprite.h" #include @@ -65,8 +64,8 @@ static bool hasPointTexs[NUM_TEX_NAMES]; struct OrderedParticle { Generator *generator; - int ptID; // Particle id. - float distance; + dint ptID; // Particle id. + dfloat distance; }; static OrderedParticle *order; static size_t orderSize; @@ -76,10 +75,10 @@ static size_t numParts; /* * Console variables: */ -byte useParticles = true; -static int maxParticles; ///< @c 0= Unlimited. -static int particleNearLimit; -static float particleDiffuse = 4; +dbyte useParticles = true; +static dint maxParticles; ///< @c 0= Unlimited. +static dint particleNearLimit; +static dfloat particleDiffuse = 4; void Rend_ParticleRegister() { @@ -90,20 +89,20 @@ void Rend_ParticleRegister() C_VAR_INT ("rend-particle-visible-near", &particleNearLimit, CVF_NO_MAX, 0, 0); } -static float pointDist(fixed_t const c[3]) +static dfloat pointDist(fixed_t const c[3]) { viewdata_t const *viewData = R_ViewData(viewPlayer - ddPlayers); - float dist = ((viewData->current.origin.y - FIX2FLT(c[VY])) * -viewData->viewSin) - - ((viewData->current.origin.x - FIX2FLT(c[VX])) * viewData->viewCos); + dfloat dist = ((viewData->current.origin.y - FIX2FLT(c[1])) * -viewData->viewSin) - + ((viewData->current.origin.x - FIX2FLT(c[0])) * viewData->viewCos); - return de::abs(dist); // Always return positive. + return de::abs(dist); // Always return positive. } static Path tryFindImage(String name) { - /* - * First look for a colorkeyed version. - */ + // + // First look for a colorkeyed version. + // try { String foundPath = App_FileSystem().findPath(de::Uri("Textures", name + "-ck"), @@ -112,11 +111,11 @@ static Path tryFindImage(String name) return App_BasePath() / foundPath; } catch(FS1::NotFoundError const&) - {} // Ignore this error. + {} // Ignore this error. - /* - * Look for the regular version. - */ + // + // Look for the regular version. + // try { String foundPath = App_FileSystem().findPath(de::Uri("Textures", name), @@ -125,13 +124,13 @@ static Path tryFindImage(String name) return App_BasePath() / foundPath; } catch(FS1::NotFoundError const&) - {} // Ignore this error. + {} // Ignore this error. - return Path(); // Not found. + return Path(); // Not found. } // Try to load the texture. -static byte loadParticleTexture(uint particleTex) +static dbyte loadParticleTexture(duint particleTex) { DENG2_ASSERT(particleTex < MAX_PTC_TEXTURES); @@ -380,22 +379,19 @@ static int listVisibleParticles(Map &map) static void setupModelParamsForParticle(vissprite_t &spr, ParticleInfo const *pinfo, GeneratorParticleStage const *st, - ded_ptcstage_t const *dst, Vector3f const &origin, float dist, float size, - float mark, float alpha) + ded_ptcstage_t const *dst, Vector3f const &origin, dfloat dist, dfloat size, + dfloat mark, dfloat alpha) { drawmodelparams_t &parm = *VS_MODEL(&spr); - // Render the particle as a model. - spr.pose.origin[VX] = origin.x; - spr.pose.origin[VY] = origin.z; - spr.pose.origin[VZ] = spr.pose.topZ = origin.y; - spr.pose.distance = dist; + spr.pose.origin = Vector3d(origin.xz(), spr.pose.topZ = origin.y); + spr.pose.distance = dist; + spr.pose.extraScale = size; // Extra scaling factor. - spr.pose.extraScale = size; // Extra scaling factor. parm.mf = &ClientApp::resourceSystem().modelDef(dst->model); parm.alwaysInterpolate = true; - int frame; + dint frame; if(dst->endFrame < 0) { frame = dst->frame; @@ -427,11 +423,11 @@ static void setupModelParamsForParticle(vissprite_t &spr, spr.pose.pitch = pinfo->pitch / 32768.0f * 180; } - spr.light.ambientColor[CA] = alpha; + spr.light.ambientColor[3] = alpha; if(st->flags.testFlag(GeneratorParticleStage::Bright) || levelFullBright) { - spr.light.ambientColor[CR] = spr.light.ambientColor[CG] = spr.light.ambientColor[CB] = 1; + spr.light.ambientColor[0] = spr.light.ambientColor[1] = spr.light.ambientColor[2] = 1; spr.light.vLightListIdx = 0; } else @@ -442,7 +438,7 @@ static void setupModelParamsForParticle(vissprite_t &spr, { Vector4f color = map.lightGrid().evaluate(spr.pose.origin); // Apply light range compression. - for(int i = 0; i < 3; ++i) + for(dint i = 0; i < 3; ++i) { color[i] += Rend_LightAdaptationDelta(color[i]); } @@ -454,7 +450,7 @@ static void setupModelParamsForParticle(vissprite_t &spr, { Vector4f const color = pinfo->bspLeaf->subspace().cluster().lightSourceColorfIntensity(); - float lightLevel = color.w; + dfloat lightLevel = color.w; // Apply distance attenuation. lightLevel = Rend_AttenuateLightLevel(spr.pose.distance, lightLevel); @@ -467,20 +463,16 @@ static void setupModelParamsForParticle(vissprite_t &spr, Rend_ApplyLightAdaptation(lightLevel); // Determine the final ambientColor. - for(int i = 0; i < 3; ++i) + for(dint i = 0; i < 3; ++i) { spr.light.ambientColor[i] = lightLevel * color[i]; } } - Rend_ApplyTorchLight(spr.light.ambientColor, spr.pose.distance); - collectaffectinglights_params_t lparams; zap(lparams); - lparams.origin = Vector3d(spr.pose.origin); - lparams.subspace = map.bspLeafAt(lparams.origin).subspacePtr(); - lparams.ambientColor = Vector3f(spr.light.ambientColor); - - spr.light.vLightListIdx = R_CollectAffectingLights(&lparams); + spr.light.vLightListIdx = + Rend_CollectAffectingLights(spr.pose.origin, spr.light.ambientColor, + map.bspLeafAt(spr.pose.origin).subspacePtr()); } } @@ -498,7 +490,7 @@ static Vector2f lineUnitVector(Line const &line) { return line.direction() / len; } - return Vector2f(0, 0); + return Vector2f(); } static void renderParticles(int rtype, bool withBlend) diff --git a/doomsday/client/src/render/rendersystem.cpp b/doomsday/client/src/render/rendersystem.cpp index c894ddea0c..c74dbe9c89 100644 --- a/doomsday/client/src/render/rendersystem.cpp +++ b/doomsday/client/src/render/rendersystem.cpp @@ -98,8 +98,8 @@ duint Store::allocateVertices(duint count) return base; } -ProjectionList::Node* ProjectionList::firstNode = nullptr; -ProjectionList::Node* ProjectionList::cursorNode = nullptr; +ProjectionList::Node *ProjectionList::firstNode = nullptr; +ProjectionList::Node *ProjectionList::cursorNode = nullptr; void ProjectionList::init() // static { @@ -112,12 +112,18 @@ void ProjectionList::init() // static } } -void ProjectionList::reset() // static +void ProjectionList::rewind() // static { // Start reusing nodes. cursorNode = firstNode; } +/// Averaged-color * alpha. +static dfloat luminosity(ProjectedTextureData const &pt) // static +{ + return (pt.color.x + pt.color.y + pt.color.z) / 3 * pt.color.w; +} + ProjectionList &ProjectionList::add(ProjectedTextureData &texp) { Node *node = newNode(); @@ -156,7 +162,7 @@ ProjectionList::Node *ProjectionList::newNode() // static { Node *node; - // Do we need to allocate mode nodes? + // Do we need to allocate more nodes? if(!cursorNode) { node = (Node *) Z_Malloc(sizeof(*node), PU_APPSTATIC, nullptr); @@ -175,10 +181,82 @@ ProjectionList::Node *ProjectionList::newNode() // static return node; } -/// Average color * alpha. -dfloat ProjectionList::luminosity(ProjectedTextureData const &texp) // static +VectorLightList::Node *VectorLightList::firstNode = nullptr; +VectorLightList::Node *VectorLightList::cursorNode = nullptr; + +void VectorLightList::init() // static +{ + static bool firstTime = true; + if(firstTime) + { + firstNode = 0; + cursorNode = 0; + firstTime = false; + } +} + +void VectorLightList::rewind() // static { - return (texp.color.x + texp.color.y + texp.color.z) / 3 * texp.color.w; + // Start reusing nodes from the first one in the list. + cursorNode = firstNode; +} + +VectorLightList &VectorLightList::add(VectorLightData &vlight) +{ + Node *node = newNode(); + node->vlight = vlight; + + if(head) + { + Node *iter = head; + Node *last = iter; + do + { + VectorLightData *vlight = &node->vlight; + + // Is this closer than the one being added? + if(node->vlight.approxDist > vlight->approxDist) + { + last = iter; + iter = iter->next; + } + else + { + // Insert it here. + node->next = last->next; + last->next = node; + return *this; + } + } while(iter); + } + + node->next = head; + head = node; + + return *this; +} + +VectorLightList::Node *VectorLightList::newNode() // static +{ + Node *node; + + // Do we need to allocate more nodes? + if(!cursorNode) + { + node = (Node *) Z_Malloc(sizeof(*node), PU_APPSTATIC, nullptr); + + // Link the new node to the list. + node->nextUsed = firstNode; + firstNode = node; + } + else + { + node = cursorNode; + cursorNode = cursorNode->nextUsed; + } + + node->next = nullptr; + return node; } DENG2_PIMPL(RenderSystem) @@ -201,7 +279,7 @@ DENG2_PIMPL(RenderSystem) duint cursorList = 0; ProjectionList *lists = nullptr; - void initForMap(Map & /*map*/) + void init() { ProjectionList::init(); @@ -213,7 +291,7 @@ DENG2_PIMPL(RenderSystem) void reset() { - ProjectionList::reset(); + ProjectionList::rewind(); // start reusing list nodes. // Clear the lists. cursorList = 0; @@ -236,7 +314,7 @@ DENG2_PIMPL(RenderSystem) { if(ProjectionList *found = tryFindList(listIdx)) return *found; /// @throw MissingListError Invalid index specified. - throw Error("ProjectionLists::findList", "Invalid index #" + String::number(listIdx)); + throw Error("RenderSystem::projector::findList", "Invalid index #" + String::number(listIdx)); } ProjectionList &findOrCreateList(duint *listIdx, bool sortByLuma) @@ -266,6 +344,77 @@ DENG2_PIMPL(RenderSystem) } } projector; + /// VectorLight => object affection lists. + struct VectorLights + { + duint listCount = 0; + duint cursorList = 0; + VectorLightList *lists = nullptr; + + void init() + { + VectorLightList::init(); + + // All memory for the lists is allocated from Zone so we can "forget" it. + lists = nullptr; + listCount = 0; + cursorList = 0; + } + + void reset() + { + VectorLightList::rewind(); // start reusing list nodes. + + // Clear the lists. + cursorList = 0; + if(listCount) + { + std::memset(lists, 0, listCount * sizeof *lists); + } + } + + VectorLightList *tryFindList(duint listIdx) const + { + if(listIdx > 0 && listIdx <= listCount) + { + return &lists[listIdx - 1]; + } + return nullptr; // not found. + } + + VectorLightList &findList(duint listIdx) const + { + if(VectorLightList *found = tryFindList(listIdx)) return *found; + /// @throw MissingListError Invalid index specified. + throw Error("RenderSystem::vlights::findList", "Invalid index #" + String::number(listIdx)); + } + + VectorLightList &findOrCreateList(duint *listIdx) + { + DENG2_ASSERT(listIdx); + + // Do we need to allocate a list? + if(!(*listIdx)) + { + // Do we need to allocate more lists? + if(++cursorList >= listCount) + { + listCount *= 2; + if(!listCount) listCount = 2; + + lists = (VectorLightList *) Z_Realloc(lists, listCount * sizeof(*lists), PU_MAP); + } + + VectorLightList *list = &lists[cursorList - 1]; + list->head = nullptr; + + *listIdx = cursorList; + } + + return lists[(*listIdx) - 1]; // 1-based index. + } + } vlights; + Instance(Public *i) : Base(i) { LOG_AS("RenderSystem"); @@ -489,27 +638,31 @@ void RenderSystem::clearDrawLists() d->buffer.clear(); } -void RenderSystem::resetDrawLists() -{ - d->drawLists.reset(); - - // Start reallocating storage from the global vertex buffer, also. - d->buffer.rewind(); -} - DrawLists &RenderSystem::drawLists() { return d->drawLists; } -void RenderSystem::projectorInitForMap(Map &map) +void RenderSystem::worldSystemMapChanged(de::Map &) { - d->projector.initForMap(map); + d->projector.init(); + d->vlights.init(); } -void RenderSystem::projectorReset() +void RenderSystem::beginFrame() { + // Clear the draw lists ready for new geometry. + d->drawLists.reset(); + d->buffer.rewind(); // Start reallocating storage from the global vertex buffer. + + // Clear the clipper - we're drawing from a new point of view. + d->clipper.clearRanges(); + + // Recycle view dependent list data. d->projector.reset(); + d->vlights.reset(); + + R_BeginFrame(); } ProjectionList &RenderSystem::findSurfaceProjectionList(duint *listIdx, bool sortByLuma) @@ -530,6 +683,24 @@ LoopResult RenderSystem::forAllSurfaceProjections(duint listIdx, std::functionvlights.findOrCreateList(listIdx); +} + +LoopResult RenderSystem::forAllVectorLights(duint listIdx, std::function func) +{ + if(VectorLightList *list = d->vlights.tryFindList(listIdx)) + { + for(VectorLightList::Node *node = list->head; node; node = node->next) + { + if(auto result = func(node->vlight)) + return result; + } + } + return LoopContinue; +} + void RenderSystem::consoleRegister() { Viewports_Register(); diff --git a/doomsday/client/src/render/viewports.cpp b/doomsday/client/src/render/viewports.cpp index 315a5f9670..1104df9c3a 100644 --- a/doomsday/client/src/render/viewports.cpp +++ b/doomsday/client/src/render/viewports.cpp @@ -1268,10 +1268,6 @@ static int lumobjSorter(void const *e1, void const *e2) void R_BeginFrame() { - // Clear the projected texture lists. This is done here as the projections - // are sensitive to distance from the viewer. - rendSys().projectorReset(); - Map &map = worldSys().map(); subspacesVisible.resize(map.subspaceCount()); @@ -1280,14 +1276,14 @@ void R_BeginFrame() // Clear all generator visibility flags. generatorsVisible.fill(false); - int numLuminous = map.lumobjCount(); + dint numLuminous = map.lumobjCount(); if(!(numLuminous > 0)) return; // Resize the associated buffers used for per-frame stuff. - int maxLuminous = numLuminous; - luminousDist = (coord_t *) M_Realloc(luminousDist, sizeof(*luminousDist) * maxLuminous); - luminousClipped = (byte *) M_Realloc(luminousClipped, sizeof(*luminousClipped) * maxLuminous); - luminousOrder = (uint *) M_Realloc(luminousOrder, sizeof(*luminousOrder) * maxLuminous); + dint maxLuminous = numLuminous; + luminousDist = (coord_t *) M_Realloc(luminousDist, sizeof(*luminousDist) * maxLuminous); + luminousClipped = (dbyte *) M_Realloc(luminousClipped, sizeof(*luminousClipped) * maxLuminous); + luminousOrder = (duint *) M_Realloc(luminousOrder, sizeof(*luminousOrder) * maxLuminous); // Update viewer => lumobj distances ready for linking and sorting. viewdata_t const *viewData = R_ViewData(viewPlayer - ddPlayers); @@ -1305,17 +1301,17 @@ void R_BeginFrame() // so that only the closest are visible (max loMaxLumobjs). // Init the lumobj indices, sort array. - for(int i = 0; i < numLuminous; ++i) + for(dint i = 0; i < numLuminous; ++i) { luminousOrder[i] = i; } - qsort(luminousOrder, numLuminous, sizeof(uint), lumobjSorter); + qsort(luminousOrder, numLuminous, sizeof(duint), lumobjSorter); // Mark all as hidden. std::memset(luminousClipped, 2, numLuminous * sizeof(*luminousClipped)); - int n = 0; - for(int i = 0; i < numLuminous; ++i) + dint n = 0; + for(dint i = 0; i < numLuminous; ++i) { if(n++ > rendMaxLumobjs) break; diff --git a/doomsday/client/src/render/vlight.cpp b/doomsday/client/src/render/vlight.cpp deleted file mode 100644 index c7ff68c8df..0000000000 --- a/doomsday/client/src/render/vlight.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/** @file vlight.cpp Vector lights - * @ingroup render - * - * @authors Copyright © 2003-2014 Jaakko Keränen - * @authors Copyright © 2005-2014 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, write to the Free - * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - */ - -#include "de_platform.h" -#include "render/vlight.h" - -#include "de_graphics.h" -#include "de_render.h" - -#include "world/map.h" -#include "ConvexSubspace" -#include "SectorCluster" -#include "Surface" - -#include "Contact" -#include -#include // M_ApproxDistance -#include - -using namespace de; - -struct ListNode -{ - ListNode *next, *nextUsed; - VectorLight vlight; -}; - -struct ProjectionList -{ - ListNode *head; -}; - -// VectorLight List nodes. -static ListNode *firstNode, *cursorNode; - -// VectorLight lists. -static uint listCount, cursorList; -static ProjectionList *lists; - -/** - * Create a new vlight list. - * @return Identifier for the new list. - */ -static uint newList() -{ - // Do we need to allocate more lists? - if(++cursorList >= listCount) - { - listCount *= 2; - if(!listCount) listCount = 2; - - lists = (ProjectionList *) Z_Realloc(lists, listCount * sizeof(*lists), PU_MAP); - } - - ProjectionList *list = &lists[cursorList - 1]; - list->head = 0; - - return cursorList - 1; -} - -static ListNode *newListNode() -{ - ListNode *node; - - // Have we run out of nodes? - if(!cursorNode) - { - node = (ListNode *) Z_Malloc(sizeof(ListNode), PU_APPSTATIC, 0); - - // Link the new node to the list. - node->nextUsed = firstNode; - firstNode = node; - } - else - { - node = cursorNode; - cursorNode = cursorNode->nextUsed; - } - - node->next = 0; - return node; -} - -/** - * @return Ptr to a new vlight node. If the list of unused nodes is empty, - * a new node is created. - */ -static ListNode *newVLight() -{ - ListNode *node = newListNode(); - //vlight_t *vlight = &node->vlight; - - /// @todo move vlight setup here. - return node; -} - -static void linkNodeInList(ListNode *node, uint listIdx) -{ - ProjectionList *list = &lists[listIdx]; - - if(list->head) - { - ListNode *iter = list->head; - ListNode *last = iter; - do - { - VectorLight *vlight = &node->vlight; - - // Is this closer than the one being added? - if(node->vlight.approxDist > vlight->approxDist) - { - last = iter; - iter = iter->next; - } - else - { // Need to insert it here. - node->next = last->next; - last->next = node; - return; - } - } while(iter); - } - - node->next = list->head; - list->head = node; -} - -void VL_InitForMap(Map &map) -{ - DENG_UNUSED(map); - lists = 0; - listCount = 0; - cursorList = 0; -} - -void VL_InitForNewFrame() -{ - // Start reusing nodes from the first one in the list. - cursorNode = firstNode; - - // Clear the mobj vlight link lists. - cursorList = 0; - if(listCount) - std::memset(lists, 0, listCount * sizeof(ProjectionList)); -} - -/** - * World light can both light and shade. Normal objects get more shade than light - * (preventing them from ending up too bright compared to the ambient light). - */ -static void lightWithWorldLight(Vector3f const &color, bool starkLight, uint listIdx) -{ - static Vector3f const worldLight(-.400891f, -.200445f, .601336f); - - ListNode *node = newVLight(); - VectorLight *vlight = &node->vlight; - - vlight->direction = worldLight; - vlight->color = color; - vlight->affectedByAmbient = false; - vlight->approxDist = 0; - - if(starkLight) - { - vlight->lightSide = .35f; - vlight->darkSide = .5f; - vlight->offset = 0; - } - else - { - vlight->lightSide = .2f; - vlight->darkSide = .8f; - vlight->offset = .3f; - } - - linkNodeInList(node, listIdx); -} - -/** - * Interpret vlights from lumobjs near the @a origin which contact the specified - * @a subspace and add them to the identified list. - * - * @param origin Point in the map being evaluated. - * @param listIdx Identifier of the vlight list to update. - */ -static void lightWithLumobjs(Vector3d const &origin, ConvexSubspace &subspace, duint listIdx) -{ - R_ForAllSubspaceLumContacts(subspace, [&origin, &listIdx] (Lumobj &lum) - { - Vector3d lumCenter = lum.origin(); - lumCenter.z += lum.zOffset(); - - // Is this light close enough? - ddouble dist = M_ApproxDistance(M_ApproxDistance(lumCenter.x - origin.x, - lumCenter.y - origin.y), - origin.z - lumCenter.z); - dfloat intensity = 0; - if(dist < Lumobj::radiusMax()) - { - intensity = de::clamp(0.f, dfloat(1 - dist / lum.radius()) * 2, 1.f); - } - - if(intensity < .05f) - return LoopContinue; - - ListNode *node = newVLight(); - VectorLight *vlight = &node->vlight; - - vlight->direction = (lumCenter - origin) / dist; - vlight->color = lum.color() * intensity; - vlight->affectedByAmbient = true; - vlight->approxDist = dist; - vlight->lightSide = 1; - vlight->darkSide = 0; - vlight->offset = 0; - - linkNodeInList(node, listIdx); - return LoopContinue; - }); -} - -/** - * Interpret vlights from glowing planes at the @a origin in the specfified - * @a subspace and add them to the identified list. - */ -static void lightWithPlaneGlows(Vector3d const &origin, ConvexSubspace &subspace, uint listIdx) -{ - SectorCluster &cluster = subspace.cluster(); - for(int i = 0; i < cluster.sector().planeCount(); ++i) - { - Plane &plane = cluster.visPlane(i); - Surface &surface = plane.surface(); - - // Glowing at this moment? - Vector3f glowColor; - float intensity = surface.glow(glowColor); - if(intensity < .05f) - continue; - - coord_t glowHeight = Rend_PlaneGlowHeight(intensity); - if(glowHeight < 2) - continue; // Not too small! - - // In front of the plane? - Vector3d pointOnPlane = Vector3d(cluster.center(), plane.heightSmoothed()); - double dist = (origin - pointOnPlane).dot(surface.normal()); - if(dist < 0) - continue; - - intensity *= 1 - dist / glowHeight; - if(intensity < .05f) - continue; - - Vector3f color = Rend_LuminousColor(glowColor, intensity); - - if(color != Vector3f(0, 0, 0)) - { - ListNode *node = newVLight(); - VectorLight *vlight = &node->vlight; - - vlight->direction = Vector3f(surface.normal().x, surface.normal().y, -surface.normal().z); - vlight->color = color; - vlight->affectedByAmbient = true; - vlight->approxDist = dist; - vlight->lightSide = 1; - vlight->darkSide = 0; - vlight->offset = .3f; - - linkNodeInList(node, listIdx); - } - } -} - -uint R_CollectAffectingLights(collectaffectinglights_params_t const *p) -{ - if(!p) return 0; - - uint listIdx = newList(); - - // Always apply an ambient world light. - lightWithWorldLight(p->ambientColor, p->starkLight, listIdx); - - // Add extra light by interpreting nearby sources. - if(p->subspace) - { - lightWithLumobjs(p->origin, *p->subspace, listIdx); - - lightWithPlaneGlows(p->origin, *p->subspace, listIdx); - } - - return listIdx + 1; -} - -int VL_ListIterator(uint listIdx, - std::function callback, - void *context) -{ - if(listIdx != 0 && listIdx <= listCount) - { - ListNode *node = lists[listIdx - 1].head; - while(node) - { - if(int result = callback(&node->vlight, context)) - { - return result; - } - node = node->next; - } - } - return false; // Continue iteration. -} - -void Rend_DrawVectorLight(VectorLight const *vlight, float alpha) -{ - if(!vlight) return; - - DENG_ASSERT_IN_MAIN_THREAD(); - DENG_ASSERT_GL_CONTEXT_ACTIVE(); - - if(alpha < .0001f) return; - - float const unitLength = 100; - - glBegin(GL_LINES); - glColor4f(vlight->color.x, vlight->color.y, vlight->color.z, alpha); - glVertex3f(unitLength * vlight->direction.x, unitLength * vlight->direction.z, unitLength * vlight->direction.y); - glColor4f(vlight->color.x, vlight->color.y, vlight->color.z, 0); - glVertex3f(0, 0, 0); - glEnd(); -} diff --git a/doomsday/client/src/world/mapobject.cpp b/doomsday/client/src/world/mapobject.cpp index 22a391b129..7949e78fbb 100644 --- a/doomsday/client/src/world/mapobject.cpp +++ b/doomsday/client/src/world/mapobject.cpp @@ -1,7 +1,7 @@ /** @file mapobject.cpp Base class for all map objects. * * @authors Copyright © 2013 Jaakko Keränen - * @authors Copyright © 2013 Daniel Swanson + * @authors Copyright © 2013-2015 Daniel Swanson * * @par License * GPL: http://www.gnu.org/licenses/gpl.html @@ -27,21 +27,16 @@ using namespace de; DENG2_PIMPL_NOREF(MapObject) { - Map *map; - int indexInMap; - Vector3d origin; ///< Position in map space. - BspLeaf *bspLeaf; ///< BSP leaf at @ref origin in the map (not owned). - - Instance(Vector3d const &origin) - : map(0) - , indexInMap(NoIndex) - , origin(origin) - , bspLeaf(0) - {} + Map *map = nullptr; + dint indexInMap = NoIndex; + Vector3d origin; ///< Position in map space. + BspLeaf *bspLeaf = nullptr; ///< BSP leaf at @ref origin in the map (not owned). }; -MapObject::MapObject(Vector3d const &origin) : d(new Instance(origin)) -{} +MapObject::MapObject(Vector3d const &origin) : d(new Instance) +{ + d->origin = origin; +} MapObject::~MapObject() {} @@ -59,7 +54,7 @@ void MapObject::setOrigin(Vector3d const &newOrigin) if(!de::fequal(d->origin.x, newOrigin.x) || !de::fequal(d->origin.y, newOrigin.y)) { - d->bspLeaf = 0; + d->bspLeaf = nullptr; } d->origin = newOrigin; @@ -83,15 +78,12 @@ BspLeaf &MapObject::bspLeafAtOrigin() const bool MapObject::hasMap() const { - return d->map != 0; + return d->map != nullptr; } Map &MapObject::map() const { - if(d->map) - { - return *d->map; - } + if(d->map) return *d->map; /// @throw MissingMapError Attempted with no map attributed. throw MissingMapError("MapObject::map", "No map is attributed"); } @@ -101,12 +93,12 @@ void MapObject::setMap(Map *newMap) d->map = newMap; } -int MapObject::indexInMap() const +dint MapObject::indexInMap() const { return d->indexInMap; } -void MapObject::setIndexInMap(int newIndex) +void MapObject::setIndexInMap(dint newIndex) { d->indexInMap = newIndex; } diff --git a/doomsday/client/src/world/worldsystem.cpp b/doomsday/client/src/world/worldsystem.cpp index 84d4b09ff8..ecbcabd318 100644 --- a/doomsday/client/src/world/worldsystem.cpp +++ b/doomsday/client/src/world/worldsystem.cpp @@ -70,7 +70,6 @@ # include "render/rend_fakeradio.h" # include "render/rend_main.h" # include "render/skydrawable.h" -# include "render/vlight.h" #endif #ifdef __SERVER__ @@ -609,8 +608,7 @@ DENG2_PIMPL(WorldSystem) map->initContactBlockmaps(); R_InitContactLists(*map); - rendSys().projectorInitForMap(*map); - VL_InitForMap(*map); // Converted vlights (from lumobjs). + rendSys().worldSystemMapChanged(*map); map->initBias(); // Shadow bias sources and surfaces. // Rewind/restart material animators.