From 187d3eb1df995aa4bce73bf71bac92ad1c061695 Mon Sep 17 00:00:00 2001 From: danij Date: Mon, 1 Mar 2010 14:45:32 +0000 Subject: [PATCH] Added: Smoothing of per-linedef lightlevel angle deltas. Approximated rounded surfaces are lit more realistically when using the DOOM lighting model. Added: CVar "rend-light-wall-angle-smooth" 1= Enable wall angle lightlevel delta smoothing (enabled by default). Changed: Default value for "rend-light-wall-angle" is now 1.2f as the new smoothing makes this less noticeable. Whilst this smoothing algorithm catches 90% of cases where it should occur there is one case that it does not. The case it doesn't handle is the same as the one case that fakeradio ommits (where a single-sided linedef is colinear with a two-sided linedef whose back sector is open). This last case cannot really be solved with the current stratergy and requires a redesign of the seg drawing loop in Rend_RenderSubsector (which I have already begun in the mapcache branch). As such I decided to leave it as is for now. I have been sitting on this idea for a while. Now that it is clear that a Beta6.9 release is necessary - I decided to quickly implement it for this release. --- doomsday/engine/data/cphelp.txt | 5 +- doomsday/engine/portable/include/p_linedef.h | 2 +- doomsday/engine/portable/include/r_world.h | 3 +- doomsday/engine/portable/include/rend_list.h | 1 - doomsday/engine/portable/src/p_linedef.c | 103 ++++++++++++++++++- doomsday/engine/portable/src/r_world.c | 21 +--- doomsday/engine/portable/src/rend_main.c | 79 ++++++++++---- 7 files changed, 169 insertions(+), 45 deletions(-) diff --git a/doomsday/engine/data/cphelp.txt b/doomsday/engine/data/cphelp.txt index beb5b26842..01eb0c6e11 100644 --- a/doomsday/engine/data/cphelp.txt +++ b/doomsday/engine/data/cphelp.txt @@ -656,7 +656,10 @@ desc = Maximum radius of dynamic lights (default: 128). desc = Minimum dynamic light radius to convert to BIAS light source. [rend-light-wall-angle] -desc = Intensity of angle-based wall light. +desc = Intensity of angle-based wall lighting delta. + +[rend-light-wall-angle-smooth] +desc = 1=Enable smoothing of angle-based wall lighting. [rend-light-multitex] desc = 1=Use multitexturing when rendering dynamic lights. diff --git a/doomsday/engine/portable/include/p_linedef.h b/doomsday/engine/portable/include/p_linedef.h index 6123889fc1..27c4a45879 100644 --- a/doomsday/engine/portable/include/p_linedef.h +++ b/doomsday/engine/portable/include/p_linedef.h @@ -32,7 +32,7 @@ #include "r_data.h" #include "p_dmu.h" -float Linedef_GetLightLevelDelta(const linedef_t* l); +void Linedef_LightLevelDelta(const linedef_t* lineDef, byte side, float* deltaL, float* deltaR); boolean Linedef_GetProperty(const linedef_t *lin, setargs_t *args); boolean Linedef_SetProperty(linedef_t *lin, const setargs_t *args); diff --git a/doomsday/engine/portable/include/r_world.h b/doomsday/engine/portable/include/r_world.h index e8591b77ac..f62c805751 100644 --- a/doomsday/engine/portable/include/r_world.h +++ b/doomsday/engine/portable/include/r_world.h @@ -47,6 +47,8 @@ typedef struct skyfix_s { } skyfix_t; extern int rendSkyLight; // cvar +extern float rendLightWallAngle; +extern byte rendLightWallAngleSmooth; extern boolean ddMapSetup; extern skyfix_t skyFix[2]; // [floor, ceiling] @@ -63,7 +65,6 @@ void R_SetupSky(ded_sky_t* sky); const float* R_GetSectorLightColor(const sector_t* sector); float R_DistAttenuateLightLevel(float distToViewer, float lightLevel); -float R_WallAngleLightLevelDelta(const linedef_t* l, byte side); float R_ExtraLightDelta(void); float R_CheckSectorLight(float lightlevel, float min, float max); boolean R_IsSkySurface(const surface_t* suf); diff --git a/doomsday/engine/portable/include/rend_list.h b/doomsday/engine/portable/include/rend_list.h index 6be409ddc5..19d8f4e25a 100644 --- a/doomsday/engine/portable/include/rend_list.h +++ b/doomsday/engine/portable/include/rend_list.h @@ -63,7 +63,6 @@ extern int renderTextures; extern int renderWireframe; extern int useMultiTexLights; extern int useMultiTexDetails; -extern float rendLightWallAngle; extern float detailFactor, detailScale; extern int torchAdditive; extern float torchColor[]; diff --git a/doomsday/engine/portable/src/p_linedef.c b/doomsday/engine/portable/src/p_linedef.c index 81ba1c10f4..c2532e90a9 100644 --- a/doomsday/engine/portable/src/p_linedef.c +++ b/doomsday/engine/portable/src/p_linedef.c @@ -32,6 +32,8 @@ #include "de_refresh.h" #include "de_play.h" +#include "m_bams.h" + // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- @@ -50,10 +52,105 @@ // CODE -------------------------------------------------------------------- -float Linedef_GetLightLevelDelta(const linedef_t* l) +static void calcNormal(const linedef_t* l, byte side, pvec2_t normal) +{ + V2_Set(normal, (l->L_vpos(side^1)[VY] - l->L_vpos(side)[VY]) / l->length, + (l->L_vpos(side)[VX] - l->L_vpos(side^1)[VX]) / l->length); +} + +static float lightLevelDelta(const pvec2_t normal) +{ + return (1.0f / 255) * (normal[VX] * 18) * rendLightWallAngle; +} + +static linedef_t* findBlendNeighbor(const linedef_t* l, byte side, byte right, + binangle_t* diff) +{ + if(!l->L_backside || + l->L_backsector->SP_ceilvisheight <= l->L_frontsector->SP_floorvisheight || + l->L_backsector->SP_floorvisheight >= l->L_frontsector->SP_ceilvisheight) + { + return R_FindSolidLineNeighbor(l->L_sector(side), l, l->L_vo(right^side), right, diff); + } + + return R_FindLineNeighbor(l->L_sector(side), l, l->L_vo(right^side), right, diff); +} + +/** + * The DOOM lighting model applies a sector light level delta when drawing + * segs based on their 2D world angle. + * + * @param l Linedef to calculate the delta for. + * @param side Side of the linedef we are interested in. + * @param deltaL Light delta for the left edge is written back here. + * @param deltaR Light delta for the right edge is written back here. + */ +void Linedef_LightLevelDelta(const linedef_t* l, byte side, float* deltaL, float* deltaR) { - return (1.0f / 255) * - ((l->L_vpos(1)[VY] - l->L_vpos(0)[VY]) / l->length * 18); + vec2_t normal; + float delta; + + // Disabled? + if(!(rendLightWallAngle > 0)) + { + *deltaL = *deltaR = 0; + return; + } + + calcNormal(l, side, normal); + delta = lightLevelDelta(normal); + + // If smoothing is disabled use this delta for left and right edges. + // Must forcibly disable smoothing for polyobj linedefs as they have + // no owner rings. + if(!rendLightWallAngleSmooth || (l->inFlags & LF_POLYOBJ)) + { + *deltaL = *deltaR = delta; + return; + } + + // Find the left neighbour linedef for which we will calculate the + // lightlevel delta and then blend with this to produce the value for + // the left edge. Blend iff the angle between the two linedefs is less + // than 45 degrees. + { + binangle_t diff = 0; + linedef_t* other = findBlendNeighbor(l, side, 0, &diff); + if(other && INRANGE_OF(diff, BANG_180, BANG_45)) + { + vec2_t otherNormal; + + calcNormal(other, other->L_v2 != l->L_v(side), otherNormal); + + // Average normals. + V2_Sum(otherNormal, otherNormal, normal); + otherNormal[VX] /= 2; otherNormal[VY] /= 2; + + *deltaL = lightLevelDelta(otherNormal); + } + else + *deltaL = delta; + } + + // Do the same for the right edge but with the right neighbour linedef. + { + binangle_t diff = 0; + linedef_t* other = findBlendNeighbor(l, side, 1, &diff); + if(other && INRANGE_OF(diff, BANG_180, BANG_45)) + { + vec2_t otherNormal; + + calcNormal(other, other->L_v1 != l->L_v(side^1), otherNormal); + + // Average normals. + V2_Sum(otherNormal, otherNormal, normal); + otherNormal[VX] /= 2; otherNormal[VY] /= 2; + + *deltaR = lightLevelDelta(otherNormal); + } + else + *deltaR = delta; + } } /** diff --git a/doomsday/engine/portable/src/r_world.c b/doomsday/engine/portable/src/r_world.c index 86336a63b7..256c238365 100644 --- a/doomsday/engine/portable/src/r_world.c +++ b/doomsday/engine/portable/src/r_world.c @@ -64,7 +64,8 @@ // PUBLIC DATA DEFINITIONS ------------------------------------------------- int rendSkyLight = 1; // cvar. -float rendLightWallAngle = 1; // Intensity of angle-based wall lighting. +float rendLightWallAngle = 1.2f; // Intensity of angle-based wall lighting. +byte rendLightWallAngleSmooth = true; boolean firstFrameAfterLoad; boolean ddMapSetup; @@ -1937,24 +1938,6 @@ float R_DistAttenuateLightLevel(float distToViewer, float lightLevel) return lightLevel; } -/** - * The DOOM lighting model applies a sector light level delta when drawing - * segs based on their 2D world angle. - * - * @param l Linedef to calculate the delta for. - * @param side Side of the linedef we are interested in. - * @return Calculated delta. - */ -float R_WallAngleLightLevelDelta(const linedef_t* l, byte side) -{ - if(!(rendLightWallAngle > 0)) - return 0; - - // Do a lighting adjustment based on orientation. - return Linedef_GetLightLevelDelta(l) * (side? -1 : 1) * - rendLightWallAngle; -} - /** * The DOOM lighting model applies a light level delta to everything when * e.g. the player shoots. diff --git a/doomsday/engine/portable/src/rend_main.c b/doomsday/engine/portable/src/rend_main.c index 1066d7fa88..3f485edbf1 100644 --- a/doomsday/engine/portable/src/rend_main.c +++ b/doomsday/engine/portable/src/rend_main.c @@ -132,6 +132,7 @@ void Rend_Register(void) C_VAR_INT2("rend-light-ambient", &ambientLight, 0, 0, 255, Rend_CalcLightModRange); C_VAR_INT2("rend-light-sky", &rendSkyLight, 0, 0, 1, LG_MarkAllForUpdate); C_VAR_FLOAT("rend-light-wall-angle", &rendLightWallAngle, CVF_NO_MAX, 0, 0); + C_VAR_BYTE("rend-light-wall-angle-smooth", &rendLightWallAngleSmooth, 0, 0, 1); C_VAR_FLOAT("rend-light-attenuation", &rendLightDistanceAttentuation, CVF_NO_MAX, 0, 0); C_VAR_INT("rend-dev-sky", &devRendSkyMode, CVF_NO_ARCHIVE, 0, 2); @@ -1363,7 +1364,8 @@ typedef struct { const float* normal; // Surface normal. float alpha; const float* sectorLightLevel; - float surfaceLightLevelDelta; + float surfaceLightLevelDL; + float surfaceLightLevelDR; const float* sectorLightColor; const float* surfaceColor; @@ -1562,15 +1564,14 @@ static boolean renderWorldPoly(rvertex_t* rvertices, uint numVertices, } else { - uint i; - float ll = *p->sectorLightLevel + - p->surfaceLightLevelDelta; + uint i; + // Calculate the color for each vertex, blended with plane color? if(p->surfaceColor[0] < 1 || p->surfaceColor[1] < 1 || p->surfaceColor[2] < 1) { - float vColor[4]; + float vColor[4]; // Blend sector light+color+surfacecolor vColor[CR] = p->surfaceColor[CR] * p->sectorLightColor[CR]; @@ -1578,21 +1579,48 @@ static boolean renderWorldPoly(rvertex_t* rvertices, uint numVertices, vColor[CB] = p->surfaceColor[CB] * p->sectorLightColor[CB]; vColor[CA] = 1; - for(i = 0; i < numVertices; ++i) - lightVertex(&rcolors[i], &rvertices[i], ll, vColor); + if(p->isWall) + { + float llL = *p->sectorLightLevel + p->surfaceLightLevelDL; + float llR = *p->sectorLightLevel + p->surfaceLightLevelDR; + + lightVertex(&rcolors[0], &rvertices[0], llL, vColor); + lightVertex(&rcolors[1], &rvertices[1], llL, vColor); + lightVertex(&rcolors[2], &rvertices[2], llR, vColor); + lightVertex(&rcolors[3], &rvertices[3], llR, vColor); + } + else + { + float ll = *p->sectorLightLevel + p->surfaceLightLevelDL; + for(i = 0; i < numVertices; ++i) + lightVertex(&rcolors[i], &rvertices[i], ll, vColor); + } } else - { - // Use sector light+color only - for(i = 0; i < numVertices; ++i) - lightVertex(&rcolors[i], &rvertices[i], ll, - p->sectorLightColor); + { // Use sector light+color only. + if(p->isWall) + { + float llL = *p->sectorLightLevel + p->surfaceLightLevelDL; + float llR = *p->sectorLightLevel + p->surfaceLightLevelDR; + + lightVertex(&rcolors[0], &rvertices[0], llL, p->sectorLightColor); + lightVertex(&rcolors[1], &rvertices[1], llL, p->sectorLightColor); + lightVertex(&rcolors[2], &rvertices[2], llR, p->sectorLightColor); + lightVertex(&rcolors[3], &rvertices[3], llR, p->sectorLightColor); + } + else + { + float ll = *p->sectorLightLevel + p->surfaceLightLevelDL; + for(i = 0; i < numVertices; ++i) + lightVertex(&rcolors[i], &rvertices[i], ll, + p->sectorLightColor); + } } // Bottom color (if different from top)? if(p->isWall && p->surfaceColor2) { - float vColor[4]; + float vColor[4]; // Blend sector light+color+surfacecolor vColor[CR] = p->surfaceColor2[CR] * p->sectorLightColor[CR]; @@ -1600,8 +1628,8 @@ static boolean renderWorldPoly(rvertex_t* rvertices, uint numVertices, vColor[CB] = p->surfaceColor2[CB] * p->sectorLightColor[CB]; vColor[CA] = 1; - lightVertex(&rcolors[0], &rvertices[0], ll, vColor); - lightVertex(&rcolors[2], &rvertices[2], ll, vColor); + lightVertex(&rcolors[0], &rvertices[0], *p->sectorLightLevel + p->surfaceLightLevelDL, vColor); + lightVertex(&rcolors[2], &rvertices[2], *p->sectorLightLevel + p->surfaceLightLevelDR, vColor); } } @@ -1820,7 +1848,8 @@ static boolean doRenderSeg(seg_t* seg, const fvertex_t* from, const fvertex_t* to, float bottom, float top, const pvec3_t normal, float alpha, - const float* lightLevel, float lightLevelDelta, + const float* lightLevel, float lightLevelDL, + float lightLevelDR, const float* lightColor, uint lightListIdx, const walldiv_t* divs, @@ -1855,7 +1884,8 @@ static boolean doRenderSeg(seg_t* seg, params.texTL = texTL; params.texBR = texBR; params.sectorLightLevel = lightLevel; - params.surfaceLightLevelDelta = lightLevelDelta; + params.surfaceLightLevelDL = lightLevelDL; + params.surfaceLightLevelDR = lightLevelDR; params.sectorLightColor = lightColor; params.surfaceColor = color; params.surfaceColor2 = color2; @@ -1979,7 +2009,7 @@ static void renderPlane(subsector_t* ssec, planetype_t type, params.texBR = texBR; params.sectorLightLevel = &sec->lightLevel; params.sectorLightColor = R_GetSectorLightColor(sec); - params.surfaceLightLevelDelta = 0; + params.surfaceLightLevelDL = params.surfaceLightLevelDR = 0; params.surfaceColor = sufColor; params.texOffset = texOffset; params.texScale = texScale; @@ -2326,11 +2356,21 @@ static boolean rendSegSection(subsector_t* ssec, seg_t* seg, applyWallHeightDivision(divs, seg, frontsec, bottom, top); } + { + float deltaL, deltaR, diff; + + Linedef_LightLevelDelta(seg->lineDef, seg->side, &deltaL, &deltaR); + + // Linear interpolation of the linedef light deltas to the edges of the seg. + diff = deltaR - deltaL; + deltaR = deltaL + ((seg->offset + seg->length) / seg->lineDef->length) * diff; + deltaL += (seg->offset / seg->lineDef->length) * diff; + solidSeg = doRenderSeg(seg, from, to, bottom, top, surface->normal, (forceOpaque? -1 : alpha), &frontsec->lightLevel, - R_WallAngleLightLevelDelta(seg->lineDef, seg->side), + deltaL, deltaR, R_GetSectorLightColor(frontsec), lightListIdx, (divs[0].num > 0 || divs[1].num > 0)? divs : NULL, @@ -2342,6 +2382,7 @@ static boolean rendSegSection(subsector_t* ssec, seg_t* seg, color, color2, seg->bsuf[section], (uint) section, &msA, inter, blended? &msB : NULL); + } } return solidSeg;