diff --git a/doomsday/client/include/render/rend_halo.h b/doomsday/client/include/render/rend_halo.h index 099ef4c5c7..e3fd065161 100644 --- a/doomsday/client/include/render/rend_halo.h +++ b/doomsday/client/include/render/rend_halo.h @@ -17,14 +17,16 @@ * http://www.gnu.org/licenses */ -#ifndef LIBDENG_RENDER_HALO_H -#define LIBDENG_RENDER_HALO_H +#ifndef DENG_CLIENT_RENDER_HALO_H +#define DENG_CLIENT_RENDER_HALO_H + +#include #include "TextureVariantSpec" -DENG_EXTERN_C int haloOccludeSpeed; -DENG_EXTERN_C int haloMode, haloRealistic, haloBright, haloSize; -DENG_EXTERN_C float haloFadeMax, haloFadeMin, minHaloSize; +DENG_EXTERN_C int haloOccludeSpeed; +DENG_EXTERN_C int haloMode, haloRealistic, haloBright, haloSize; +DENG_EXTERN_C float haloFadeMax, haloFadeMin, minHaloSize; void H_Register(); @@ -40,9 +42,7 @@ void H_SetupState(bool dosetup); * * The caller must check that @c sourcevis, really has a ->light! (? -jk) * - * @param x X coordinate of the center of the halo. - * @param y Y coordinate of the center of the halo. - * @param z Z coordinate of the center of the halo. + * @param origin Origin of the halo in map space. * @param size The precalculated radius of the primary halo. * @param tex Texture to use for the halo. * @param color Color for the halo. @@ -59,8 +59,8 @@ void H_SetupState(bool dosetup); * * @return @c true, iff a halo was rendered. */ -bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, - DGLuint tex, float const color[3], +bool H_RenderHalo(de::Vector3d const &origin, float size, + DGLuint tex, de::Vector3f const &color, coord_t distanceToViewer, float occlusionFactor, float brightnessFactor, float viewXOffset, bool primary, bool viewRelativeRotate); @@ -68,4 +68,4 @@ bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, // Console commands. D_CMD(FlareConfig); -#endif /* LIBDENG_RENDER_HALO_H */ +#endif // DENG_CLIENT_RENDER_HALO_H diff --git a/doomsday/client/include/render/vissprite.h b/doomsday/client/include/render/vissprite.h index e372c32aef..cbe04377c0 100644 --- a/doomsday/client/include/render/vissprite.h +++ b/doomsday/client/include/render/vissprite.h @@ -18,11 +18,10 @@ * 02110-1301 USA */ +#ifdef __CLIENT__ #ifndef DENG_CLIENT_RENDER_VISSPRITE_H #define DENG_CLIENT_RENDER_VISSPRITE_H -#ifdef __CLIENT__ - #include #include "resource/r_data.h" @@ -107,7 +106,7 @@ typedef struct vissprite_s { struct vissprite_s *prev, *next; visspritetype_t type; // VSPR_* type of vissprite. coord_t distance; // Vissprites are sorted by distance. - coord_t origin[3]; + de::Vector3d origin; // An anonymous union for the data. union vissprite_data_u { @@ -187,10 +186,9 @@ DENG_EXTERN_C vispsprite_t visPSprites[DDMAXPSPRITES]; /// To be called at the start of the current render frame to clear the vissprite list. void R_ClearVisSprites(); -vissprite_t *R_NewVisSprite(); +vissprite_t *R_NewVisSprite(visspritetype_t type); void R_SortVisSprites(); -#endif // __CLIENT__ - #endif // DENG_CLIENT_RENDER_VISSPRITE_H +#endif // __CLIENT__ diff --git a/doomsday/client/src/render/r_things.cpp b/doomsday/client/src/render/r_things.cpp index 48c4741d08..96ee724da0 100644 --- a/doomsday/client/src/render/r_things.cpp +++ b/doomsday/client/src/render/r_things.cpp @@ -136,12 +136,12 @@ static int findMobjZOriginWorker(Sector *sector, void *parameters) if(p->floorAdjust && p->mo->origin[VZ] == sector->floor().height()) { - p->vis->origin[VZ] = sector->floor().visHeight(); + p->vis->origin.z = sector->floor().visHeight(); } if(p->mo->origin[VZ] + p->mo->height == sector->ceiling().height()) { - p->vis->origin[VZ] = sector->ceiling().visHeight() - p->mo->height; + p->vis->origin.z = sector->ceiling().visHeight() - p->mo->height; } return false; // Continue iteration. @@ -261,12 +261,10 @@ void R_ProjectSprite(mobj_t *mo) } // Store information in a vissprite. - vissprite_t *vis = R_NewVisSprite(); - vis->type = mf? VSPR_MODEL : VSPR_SPRITE; - vis->origin[VX] = moPos.x; - vis->origin[VY] = moPos.y; - vis->origin[VZ] = moPos.z; - vis->distance = distFromEye; + vissprite_t *vis = R_NewVisSprite(mf? VSPR_MODEL : VSPR_SPRITE); + + vis->origin = moPos; + vis->distance = distFromEye; /* * The Z origin of the visual should match that of the mobj. When smoothing @@ -281,7 +279,7 @@ void R_ProjectSprite(mobj_t *mo) findMobjZOrigin(mo, floorAdjust, vis); } - coord_t gzt = vis->origin[VZ] + -tex.origin().y; + coord_t gzt = vis->origin.z + -tex.origin().y; // Determine floor clipping. coord_t floorClip = mo->floorClip; @@ -326,7 +324,7 @@ void R_ProjectSprite(mobj_t *mo) if(mf->testSubFlag(0, MFF_ALIGN_PITCH)) { viewdata_t const *viewData = R_ViewData(viewPlayer - ddPlayers); - Vector2d delta((vis->origin[VZ] + gzt) / 2 - viewData->current.origin[VZ], distFromEye); + Vector2d delta((vis->origin.z + gzt) / 2 - viewData->current.origin[VZ], distFromEye); pitch = -BANG2DEG(bamsAtan2(delta.x * 10, delta.y * 10)); } @@ -394,7 +392,7 @@ void R_ProjectSprite(mobj_t *mo) // Adjust by the floor clip. gzt -= floorClip; - Vector3d const origin(vis->origin[VX], vis->origin[VY], gzt - ms.height() / 2.0f); + Vector3d const origin(vis->origin.x, vis->origin.y, gzt - ms.height() / 2.0f); Vector4f ambientColor; uint vLightListIdx = 0; evaluateLighting(origin, mo->bspLeaf, vis->distance, fullbright, @@ -412,15 +410,14 @@ void R_ProjectSprite(mobj_t *mo) } else { - Vector3d const origin(vis->origin); Vector4f ambientColor; uint vLightListIdx = 0; - evaluateLighting(origin, mo->bspLeaf, vis->distance, fullbright, + evaluateLighting(vis->origin, mo->bspLeaf, vis->distance, fullbright, ambientColor, &vLightListIdx); ambientColor.w = alpha; - VisSprite_SetupModel(vis->data.model, origin, vis->distance, + VisSprite_SetupModel(vis->data.model, vis->origin, vis->distance, Vector3d(visOff.x, visOff.y, visOff.z - floorClip), gzt, yaw, 0, pitch, 0, mf, nextmf, interp, @@ -450,15 +447,13 @@ void R_ProjectSprite(mobj_t *mo) Lumobj const *lum = mo->bspLeaf->map().lumobj(mo->lumIdx); - vissprite_t *vis = R_NewVisSprite(); - vis->type = VSPR_FLARE; + vissprite_t *vis = R_NewVisSprite(VSPR_FLARE); + vis->distance = distFromEye; // Determine the exact center of the flare. - Vector3d center = moPos + visOff; - vis->origin[VX] = center.x; - vis->origin[VY] = center.y; - vis->origin[VZ] = center.z + lum->zOffset(); + vis->origin = moPos + visOff; + vis->origin.z += lum->zOffset(); float flareSize = pl->brightMul; // X offset to the flare position. diff --git a/doomsday/client/src/render/rend_decor.cpp b/doomsday/client/src/render/rend_decor.cpp index 4281c03c68..dc56d4be51 100644 --- a/doomsday/client/src/render/rend_decor.cpp +++ b/doomsday/client/src/render/rend_decor.cpp @@ -82,156 +82,12 @@ static void recycleSources() sourceCursor = sourceFirst; } -/** - * @return @c > 0 if @a lightlevel passes the min max limit condition. - */ -static float checkSectorLightLevel(float lightlevel, float min, float max) -{ - // Has a limit been set? - if(min == max) return 1; - return de::clamp(0.f, (lightlevel - min) / float(max - min), 1.f); -} - -static void projectSource(Decoration const &src) -{ - MaterialSnapshot::Decoration const *decor = src.decor; - - // Don't project decorations which emit no color. - if(decor->color.x == 0 && decor->color.y == 0 && decor->color.z == 0) return; - - // Does it pass the sector light limitation? - float min = decor->lightLevels[0]; - float max = decor->lightLevels[1]; - - float lightLevel = src.bspLeaf->sector().lightLevel(); - Rend_ApplyLightAdaptation(lightLevel); - - float brightness = checkSectorLightLevel(lightLevel, min, max); - if(!(brightness > 0)) return; - - if(src.fadeMul <= 0) return; - - // Is the point in range? - double distance = Rend_PointDist3D(src.origin); - if(distance > src.maxDistance) return; - - Lumobj *lum = src.surface->map().lumobj(src.lumIdx); - if(!lum) return; // Huh? - - /* - * Light decorations become flare-type vissprites. - */ - vissprite_t *vis = R_NewVisSprite(); - vis->type = VSPR_FLARE; - - V3d_Set(vis->origin, src.origin.x, src.origin.y, src.origin.z); - vis->distance = distance; - - vis->data.flare.isDecoration = true; - vis->data.flare.lumIdx = src.lumIdx; - - // Color is taken from the associated lumobj. - V3f_Set(vis->data.flare.color, lum->color().x, lum->color().y, lum->color().z); - - if(decor->haloRadius > 0) - vis->data.flare.size = de::max(1.f, decor->haloRadius * 60 * (50 + haloSize) / 100.0f); - else - vis->data.flare.size = 0; - - if(decor->flareTex != 0) - { - vis->data.flare.tex = decor->flareTex; - } - else - { // Primary halo disabled. - vis->data.flare.flags |= RFF_NO_PRIMARY; - vis->data.flare.tex = 0; - } - - // Halo brightness drops as the angle gets too big. - vis->data.flare.mul = 1; - if(decor->elevation < 2 && decorLightFadeAngle > 0) // Close the surface? - { - Vector3d const eye(vOrigin[VX], vOrigin[VZ], vOrigin[VY]); - Vector3d const vecFromOriginToEye = (src.origin - eye).normalize(); - - float dot = float( -src.surface->normal().dot(vecFromOriginToEye) ); - if(dot < decorLightFadeAngle / 2) - { - vis->data.flare.mul = 0; - } - else if(dot < 3 * decorLightFadeAngle) - { - vis->data.flare.mul = (dot - decorLightFadeAngle / 2) - / (2.5f * decorLightFadeAngle); - } - } -} - void Rend_DecorInitForMap(Map &map) { DENG_UNUSED(map); recycleSources(); } -void Rend_DecorProject() -{ - if(!useLightDecorations) return; - - for(Decoration *src = sourceFirst; src != sourceCursor; src = src->next) - { - projectSource(*src); - } -} - -static void addLuminousDecoration(Decoration &src) -{ - MaterialSnapshot::Decoration const *decor = src.decor; - - // Don't add decorations which emit no color. - if(decor->color == Vector3f(0, 0, 0)) - return; - - // Does it pass the sector light limitation? - float lightLevel = src.bspLeaf->sector().lightLevel(); - Rend_ApplyLightAdaptation(lightLevel); - float intensity = checkSectorLightLevel(lightLevel, decor->lightLevels[0], decor->lightLevels[1]); - - if(intensity < .0001f) - return; - - // Apply the brightness factor (was calculated using sector lightlevel). - src.fadeMul = intensity * decorLightBrightFactor; - src.lumIdx = Lumobj::NoIndex; - - if(src.fadeMul <= 0) - return; - - Lumobj &lum = src.surface->map().addLumobj( - Lumobj(src.origin, decor->radius, decor->color * src.fadeMul, src.maxDistance)); - - // Any lightmaps to configure? - lum.setLightmap(Lumobj::Side, decor->tex) - .setLightmap(Lumobj::Down, decor->floorTex) - .setLightmap(Lumobj::Up, decor->ceilTex); - - // Remember it's unique index in the decoration for the purpose of drawing - // halos/lens-flares. - src.lumIdx = lum.indexInMap(); -} - -void Rend_DecorAddLuminous() -{ - if(useLightDecorations && sourceFirst != sourceCursor) - { - Decoration *src = sourceFirst; - do - { - addLuminousDecoration(*src); - } while((src = src->next) != sourceCursor); - } -} - /** * Create a new decoration source. */ @@ -297,7 +153,7 @@ static void newSource(Surface const &suf, Surface::DecorSource const &dec) static uint generateDecorLights(MaterialSnapshot::Decoration const &decor, Vector2i const &patternOffset, Vector2i const &patternSkip, Surface &suf, - Material &material, Vector3d const &v1, Vector3d const &/*v2*/, + Material &material, Vector3d const &topLeft_, Vector3d const &/*bottomRight*/, Vector2d sufDimensions, Vector3d const &delta, int axis, Vector2f const &matOffset, Sector *containingSector) { @@ -309,7 +165,7 @@ static uint generateDecorLights(MaterialSnapshot::Decoration const &decor, if(repeat == Vector2f(0, 0)) return 0; - Vector3d topLeft = v1 + suf.normal() * decor.elevation; + Vector3d topLeft = topLeft_ + suf.normal() * decor.elevation; float s = de::wrap(decor.pos[0] - material.width() * patternOffset.x + matOffset.x, 0.f, repeat.x); @@ -356,9 +212,9 @@ static uint generateDecorLights(MaterialSnapshot::Decoration const &decor, * Generate decorations for the specified surface. */ static void updateSurfaceDecorations(Surface &suf, Vector2f const &offset, - Vector3d const &v1, Vector3d const &v2, Sector *sec = 0) + Vector3d const &topLeft, Vector3d const &bottomRight, Sector *sec = 0) { - Vector3d delta = v2 - v1; + Vector3d delta = bottomRight - topLeft; if(de::fequal(delta.length(), 0)) return; int const axis = suf.normal().maxAxis(); @@ -388,7 +244,7 @@ static void updateSurfaceDecorations(Surface &suf, Vector2f const &offset, MaterialDecoration const *def = decorations[i]; generateDecorLights(decor, def->patternOffset(), def->patternSkip(), - suf, suf.material(), v1, v2, sufDimensions, + suf, suf.material(), topLeft, bottomRight, sufDimensions, delta, axis, offset, sec); } } @@ -483,3 +339,143 @@ void Rend_DecorBeginFrame() plotSourcesForSurface(*surface); } } + +/** + * @return @c > 0 if @a lightlevel passes the min max limit condition. + */ +static float checkSectorLightLevel(float lightlevel, float min, float max) +{ + // Has a limit been set? + if(min == max) return 1; + return de::clamp(0.f, (lightlevel - min) / float(max - min), 1.f); +} + +static void addLuminousDecoration(Decoration &src) +{ + MaterialSnapshot::Decoration const *decor = src.decor; + + // Don't add decorations which emit no color. + if(decor->color == Vector3f(0, 0, 0)) + return; + + // Does it pass the sector light limitation? + float lightLevel = src.bspLeaf->sector().lightLevel(); + Rend_ApplyLightAdaptation(lightLevel); + float intensity = checkSectorLightLevel(lightLevel, decor->lightLevels[0], decor->lightLevels[1]); + + if(intensity < .0001f) + return; + + // Apply the brightness factor (was calculated using sector lightlevel). + src.fadeMul = intensity * decorLightBrightFactor; + src.lumIdx = Lumobj::NoIndex; + + if(src.fadeMul <= 0) + return; + + Lumobj &lum = src.surface->map().addLumobj( + Lumobj(src.origin, decor->radius, decor->color * src.fadeMul, src.maxDistance)); + + // Any lightmaps to configure? + lum.setLightmap(Lumobj::Side, decor->tex) + .setLightmap(Lumobj::Down, decor->floorTex) + .setLightmap(Lumobj::Up, decor->ceilTex); + + // Remember it's unique index in the decoration for the purpose of drawing + // halos/lens-flares. + src.lumIdx = lum.indexInMap(); +} + +void Rend_DecorAddLuminous() +{ + if(useLightDecorations && sourceFirst != sourceCursor) + { + Decoration *src = sourceFirst; + do + { + addLuminousDecoration(*src); + } while((src = src->next) != sourceCursor); + } +} + +static void projectSource(Decoration const &src) +{ + Surface const &surface = *src.surface; + MaterialSnapshot::Decoration const *decor = src.decor; + + Lumobj *lum = surface.map().lumobj(src.lumIdx); + if(!lum) return; // Huh? + + // Is the point in range? + double distance = Rend_PointDist3D(lum->origin()); + if(R_ViewerLumobjDistance(lum->indexInMap()) > lum->maxDistance()) + return; + + // Does it pass the sector light limitation? + float min = decor->lightLevels[0]; + float max = decor->lightLevels[1]; + + float lightLevel = lum->bspLeafAtOrigin().sector().lightLevel(); + Rend_ApplyLightAdaptation(lightLevel); + + float brightness = checkSectorLightLevel(lightLevel, min, max); + if(!(brightness > 0)) return; + + /* + * Light decorations become flare-type vissprites. + */ + vissprite_t *vis = R_NewVisSprite(VSPR_FLARE); + + vis->origin = lum->origin(); + vis->distance = distance; + + vis->data.flare.lumIdx = lum->indexInMap(); + vis->data.flare.isDecoration = true; + + // Color is taken from the associated lumobj. + V3f_Set(vis->data.flare.color, lum->color().x, lum->color().y, lum->color().z); + + if(decor->haloRadius > 0) + vis->data.flare.size = de::max(1.f, decor->haloRadius * 60 * (50 + haloSize) / 100.0f); + else + vis->data.flare.size = 0; + + if(decor->flareTex != 0) + { + vis->data.flare.tex = decor->flareTex; + } + else + { // Primary halo disabled. + vis->data.flare.flags |= RFF_NO_PRIMARY; + vis->data.flare.tex = 0; + } + + // Halo brightness drops as the angle gets too big. + vis->data.flare.mul = 1; + if(decor->elevation < 2 && decorLightFadeAngle > 0) // Close the surface? + { + Vector3d const eye(vOrigin[VX], vOrigin[VZ], vOrigin[VY]); + Vector3d const vecFromOriginToEye = (lum->origin() - eye).normalize(); + + float dot = float( -surface.normal().dot(vecFromOriginToEye) ); + if(dot < decorLightFadeAngle / 2) + { + vis->data.flare.mul = 0; + } + else if(dot < 3 * decorLightFadeAngle) + { + vis->data.flare.mul = (dot - decorLightFadeAngle / 2) + / (2.5f * decorLightFadeAngle); + } + } +} + +void Rend_DecorProject() +{ + if(!useLightDecorations) return; + + for(Decoration *src = sourceFirst; src != sourceCursor; src = src->next) + { + projectSource(*src); + } +} diff --git a/doomsday/client/src/render/rend_halo.cpp b/doomsday/client/src/render/rend_halo.cpp index e91f0daef7..878b0d7a3d 100644 --- a/doomsday/client/src/render/rend_halo.cpp +++ b/doomsday/client/src/render/rend_halo.cpp @@ -33,12 +33,15 @@ #define NUM_FLARES 5 -typedef struct flare_s { - float offset; - float size; - float alpha; - int texture; // 0=round, 1=flare, 2=brflare, 3=bigflare -} flare_t; +using namespace de; + +struct flare_t +{ + float offset; + float size; + float alpha; + int texture; // 0=round, 1=flare, 2=brflare, 3=bigflare +}; D_CMD(FlareConfig); @@ -106,23 +109,33 @@ void H_SetupState(bool dosetup) } } -bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, - DGLuint tex, float const color[3], coord_t distanceToViewer, - float occlusionFactor, float brightnessFactor, float viewXOffset, - bool primary, bool viewRelativeRotate) +static inline float distanceDimFactorAt(coord_t distToViewer, float size) { - int i, k; - float viewToCenter[3], mirror[3], normalViewToCenter[3]; - float leftOff[3], rightOff[3], center[3], radius; - float haloPos[3]; - float rgba[4], radX, radY, scale, turnAngle = 0; - float fadeFactor = 1, secBold, secDimFactor; - float colorAverage, f, distanceDim; - flare_t *fl; - viewdata_t const *viewData = R_ViewData(viewPlayer - ddPlayers); + if(haloDimStart && haloDimStart < haloDimEnd && + distToViewer / size > haloDimStart) + { + return 1 - (distToViewer / size - haloDimStart) / (haloDimEnd - haloDimStart); + } + return 1; +} +static inline float fadeFactorAt(coord_t distToViewer) +{ + if(haloFadeMax && haloFadeMax != haloFadeMin && + distToViewer < haloFadeMax && distToViewer >= haloFadeMin) + { + return (distToViewer - haloFadeMin) / (haloFadeMax - haloFadeMin); + } + return 1; +} + +bool H_RenderHalo(Vector3d const &origin, float size, DGLuint tex, + Vector3f const &color, coord_t distanceToViewer, + float occlusionFactor, float brightnessFactor, float viewXOffset, + bool doPrimary, bool viewRelativeRotate) +{ // In realistic mode we don't render secondary halos. - if(!primary && haloRealistic) + if(!doPrimary && haloRealistic) return false; if(distanceToViewer <= 0 || occlusionFactor == 0 || @@ -131,61 +144,39 @@ bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, occlusionFactor = (1 + occlusionFactor) / 2; - if(haloFadeMax && haloFadeMax != haloFadeMin && - distanceToViewer < haloFadeMax && distanceToViewer >= haloFadeMin) - { - fadeFactor = (distanceToViewer - haloFadeMin) / (haloFadeMax - haloFadeMin); - } - // viewSideVec is to the left. - for(i = 0; i < 3; ++i) - { - leftOff[i] = viewData->upVec[i] + viewData->sideVec[i]; - rightOff[i] = viewData->upVec[i] - viewData->sideVec[i]; - } - - rgba[CR] = color[CR]; - rgba[CG] = color[CG]; - rgba[CB] = color[CB]; - rgba[CA] = 1; // Real alpha is set later. - - center[VX] = x; - center[VZ] = y; - center[VY] = z; + viewdata_t const *viewData = R_ViewData(viewPlayer - ddPlayers); + Vector3f const leftOff = Vector3f(viewData->upVec) + Vector3f(viewData->sideVec); + Vector3f const rightOff = Vector3f(viewData->upVec) - Vector3f(viewData->sideVec); + // Calculate the center of the flare. // Apply the flare's X offset. (Positive is to the right.) - for(i = 0; i < 3; i++) - center[i] -= viewXOffset * viewData->sideVec[i]; + Vector3f const center = Vector3f(origin.x, origin.z, origin.y) + - Vector3f(viewData->sideVec) * viewXOffset; // Calculate the mirrored position. // Project viewtocenter vector onto viewSideVec. - for(i = 0; i < 3; ++i) - { - normalViewToCenter[i] = viewToCenter[i] = center[i] - (float)(vOrigin[i]); - } - V3f_Normalize(normalViewToCenter); - - // Calculate the dimming factor for secondary flares. - secDimFactor = V3f_DotProduct(normalViewToCenter, viewData->frontVec); + Vector3f const viewToCenter = center - Vector3d(vOrigin); - scale = V3f_DotProduct(viewToCenter, viewData->frontVec) / V3f_DotProduct(viewData->frontVec, viewData->frontVec); + // Calculate the 'mirror' vector. + float const scale = viewToCenter.dot(viewData->frontVec) + / Vector3f(viewData->frontVec).dot(viewData->frontVec); + Vector3f const mirror = + (Vector3f(viewData->frontVec) * scale - viewToCenter) * 2; - for(i = 0; i < 3; ++i) - haloPos[i] = mirror[i] = (viewData->frontVec[i] * scale - viewToCenter[i]) * 2; - // Now adding 'mirror' to a position will mirror it. + // Calculate dimming factors. + float const fadeFactor = fadeFactorAt(distanceToViewer); + float const secFadeFactor = viewToCenter.normalize().dot(viewData->frontVec); // Calculate texture turn angle. - if(V3f_Normalize(haloPos)) + float turnAngle = 0; + if(viewRelativeRotate) { - // Now halopos is a normalized version of the mirror vector. - // Both vectors are on the view plane. - if(viewRelativeRotate) + // Normalize the mirror vector so that both are on the view plane. + Vector3f haloPos = mirror.normalize(); + if(haloPos.length()) { - turnAngle = V3f_DotProduct(haloPos, viewData->upVec); - if(turnAngle > 1) - turnAngle = 1; - else if(turnAngle < -1) - turnAngle = -1; + turnAngle = de::clamp(-1, haloPos.dot(viewData->upVec), 1); if(turnAngle >= 1) turnAngle = 0; @@ -195,27 +186,19 @@ bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, turnAngle = acos(turnAngle); // On which side of the up vector (left or right)? - if(V3f_DotProduct(haloPos, viewData->sideVec) < 0) + if(haloPos.dot(viewData->sideVec) < 0) turnAngle = -turnAngle; } - else - { - turnAngle = 0; - } } - // The overall brightness of the flare. - colorAverage = (rgba[CR] + rgba[CG] + rgba[CB] + 1) / 4; + // The overall brightness of the flare (average color). + float const luminosity = (color.x + color.y + color.z) / 3; // Small flares have stronger dimming. - f = distanceToViewer / size; - if(haloDimStart && haloDimStart < haloDimEnd && f > haloDimStart) - distanceDim = 1 - (f - haloDimStart) / (haloDimEnd - haloDimStart); - else - distanceDim = 1; + float const distanceDim = distanceDimFactorAt(distanceToViewer, size); // Setup GL state. - if(primary) + if(doPrimary) H_SetupState(true); DENG_ASSERT_IN_MAIN_THREAD(); @@ -230,44 +213,56 @@ bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, glRotatef(turnAngle / float(de::PI) * 180, 0, 0, 1); glTranslatef(-0.5f, -0.5f, 0); - for(i = 0, fl = flares; i < haloMode && i < NUM_FLARES; ++i, fl++) + flare_t *fl = flares; + for(int i = 0; i < haloMode && i < NUM_FLARES; ++i, fl++) { - if(primary && i) + bool const secondary = i != 0; + + if(doPrimary && secondary) break; - if(!primary && !i) + if(!doPrimary && !secondary) continue; - // Calculate the dimming factor. - if(i > 0) - // Secondary flares receive additional dimming. - f = MAX_OF(minHaloSize * size / distanceToViewer, 1); - else - f = 1; - f *= distanceDim * brightnessFactor; + // Determine visibility. + float alpha = fl->alpha * occlusionFactor * fadeFactor + + luminosity * luminosity / 5; - // The rgba & alpha of the flare. - rgba[CA] = f * (fl->alpha * occlusionFactor * fadeFactor + colorAverage * colorAverage / 5); + // Apply a dimming factor (secondary flares receive stronger dimming). + alpha *= (!secondary? 1 : de::max(minHaloSize * size / distanceToViewer, 1)) + * distanceDim * brightnessFactor; - radius = size * (1 - colorAverage / 3) + distanceToViewer / haloZMagDiv; - if(radius < haloMinRadius) - radius = haloMinRadius; - radius *= occlusionFactor; + // Apply the global dimming factor. + alpha *= .8f * haloBright / 100.0f; - secBold = colorAverage - 8 * (1 - secDimFactor); + // Secondary flares are a little bolder. + if(secondary) + { + alpha *= luminosity - 8 * (1 - secFadeFactor); + } - rgba[CA] *= .8f * haloBright / 100.0f; - if(i) + // In the realistic mode, halos are slightly dimmer. + if(haloRealistic) { - rgba[CA] *= secBold; // Secondary flare boldness. + alpha *= .6f; } - if(rgba[CA] <= 0) - break; // Not visible. + // Not visible? + if(alpha <= 0) + break; - // In the realistic mode, halos are slightly dimmer. + // Determine radius. + float radius = size * (1 - luminosity / 3) + + distanceToViewer / haloZMagDiv; + + if(radius < haloMinRadius) + radius = haloMinRadius; + + radius *= occlusionFactor; + + // In the realistic mode, halos are slightly smaller. if(haloRealistic) { - rgba[CA] *= .6f; + radius *= 0.8f; } if(haloRealistic) @@ -279,20 +274,20 @@ bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, } else { - if(!(primary && tex)) + if(!(doPrimary && tex)) { - if(size > 45 || (colorAverage > .90 && size > 20)) + if(size > 45 || (luminosity > .90 && size > 20)) { // The "Very Bright" condition. radius *= .65f; - if(!i) + if(!secondary) tex = GL_PrepareSysFlaremap(FXT_BIGFLARE); else tex = GL_PrepareSysFlaremap(flaretexid_t(fl->texture)); } else { - if(!i) + if(!secondary) tex = GL_PrepareSysFlaremap(FXT_ROUND); else tex = GL_PrepareSysFlaremap(flaretexid_t(fl->texture)); @@ -300,49 +295,39 @@ bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, } } - // In the realistic mode, halos are slightly smaller. - if(haloRealistic) - { - radius *= 0.8f; - } + // Determine the final position of the flare. + Vector3f pos = center; - // The final radius. - radX = radius * fl->size; - radY = radX / 1.2f; - - // Determine the final position of the halo. - haloPos[VX] = center[VX]; - haloPos[VY] = center[VY]; - haloPos[VZ] = center[VZ]; - if(i) - { // Secondary halos. - // Mirror it according to the flare table. - for(k = 0; k < 3; ++k) - haloPos[k] += mirror[k] * fl->offset; + // Secondary halos are mirrored according to the flare table. + if(secondary) + { + pos += mirror * fl->offset; } GL_BindTextureUnmanaged(renderTextures? tex : 0, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); glEnable(GL_TEXTURE_2D); - glColor4fv(rgba); + float const radX = radius * fl->size; + float const radY = radX / 1.2f; // Aspect correction. + glColor4f(color.x, color.y, color.z, alpha); glBegin(GL_QUADS); glTexCoord2f(0, 0); - glVertex3f(haloPos[VX] + radX * leftOff[VX], - haloPos[VY] + radY * leftOff[VY], - haloPos[VZ] + radX * leftOff[VZ]); + glVertex3f(pos.x + radX * leftOff.x, + pos.y + radY * leftOff.y, + pos.z + radX * leftOff.z); glTexCoord2f(1, 0); - glVertex3f(haloPos[VX] + radX * rightOff[VX], - haloPos[VY] + radY * rightOff[VY], - haloPos[VZ] + radX * rightOff[VZ]); + glVertex3f(pos.x + radX * rightOff.x, + pos.y + radY * rightOff.y, + pos.z + radX * rightOff.z); glTexCoord2f(1, 1); - glVertex3f(haloPos[VX] - radX * leftOff[VX], - haloPos[VY] - radY * leftOff[VY], - haloPos[VZ] - radX * leftOff[VZ]); + glVertex3f(pos.x - radX * leftOff.x, + pos.y - radY * leftOff.y, + pos.z - radX * leftOff.z); glTexCoord2f(0, 1); - glVertex3f(haloPos[VX] - radX * rightOff[VX], - haloPos[VY] - radY * rightOff[VY], - haloPos[VZ] - radX * rightOff[VZ]); + glVertex3f(pos.x - radX * rightOff.x, + pos.y - radY * rightOff.y, + pos.z - radX * rightOff.z); glEnd(); glDisable(GL_TEXTURE_2D); @@ -352,7 +337,7 @@ bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size, glPopMatrix(); // Restore previous GL state. - if(primary) + if(doPrimary) H_SetupState(false); return true; diff --git a/doomsday/client/src/render/rend_main.cpp b/doomsday/client/src/render/rend_main.cpp index 8b31acddbf..2aa3d82ae5 100644 --- a/doomsday/client/src/render/rend_main.cpp +++ b/doomsday/client/src/render/rend_main.cpp @@ -608,12 +608,9 @@ void Rend_AddMaskedPoly(Vector3f const *rvertices, Vector4f const *rcolors, coord_t wallLength, MaterialVariant *material, Vector2f const &materialOrigin, blendmode_t blendMode, uint lightListIdx, float glow) { - vissprite_t *vis = R_NewVisSprite(); + vissprite_t *vis = R_NewVisSprite(VSPR_MASKED_WALL); - vis->type = VSPR_MASKED_WALL; - vis->origin[VX] = (rvertices[0].x + rvertices[3].x) / 2; - vis->origin[VY] = (rvertices[0].y + rvertices[3].y) / 2; - vis->origin[VZ] = (rvertices[0].z + rvertices[3].z) / 2; + vis->origin = (rvertices[0] + rvertices[3]) / 2; vis->distance = Rend_PointDist2D(vis->origin); VS_WALL(vis)->texOffset[0] = materialOrigin[VX]; diff --git a/doomsday/client/src/render/sprite.cpp b/doomsday/client/src/render/sprite.cpp index c6f6330c90..b291e2e76d 100644 --- a/doomsday/client/src/render/sprite.cpp +++ b/doomsday/client/src/render/sprite.cpp @@ -745,7 +745,7 @@ static void setupModelParamsForVisPSprite(rendmodelparams_t *params, vispsprite_ } } -static boolean generateHaloForVisSprite(vissprite_t const *spr, boolean primary) +static bool generateHaloForVisSprite(vissprite_t const *spr, bool primary = false) { float occlusionFactor; @@ -765,14 +765,14 @@ static boolean generateHaloForVisSprite(vissprite_t const *spr, boolean primary) occlusionFactor = (spr->data.flare.factor & 0x7f) / 127.0f; } - return H_RenderHalo(spr->origin[VX], spr->origin[VY], spr->origin[VZ], + return H_RenderHalo(spr->origin, spr->data.flare.size, spr->data.flare.tex, spr->data.flare.color, spr->distance, occlusionFactor, spr->data.flare.mul, spr->data.flare.xOff, primary, - (spr->data.flare.flags & RFF_NO_TURN)); + (spr->data.flare.flags & RFF_NO_TURN) != 0); } void Rend_DrawMasked(void) @@ -783,7 +783,7 @@ void Rend_DrawMasked(void) if(visSpriteP > visSprites) { - boolean flareDrawn = false; + bool primaryHaloDrawn = false; // Draw all vissprites back to front. // Sprites look better with Z buffer writes turned off. @@ -808,25 +808,25 @@ void Rend_DrawMasked(void) break; case VSPR_FLARE: - if(generateHaloForVisSprite(spr, true) && !flareDrawn) + if(generateHaloForVisSprite(spr, true) && !primaryHaloDrawn) { - flareDrawn = true; + primaryHaloDrawn = true; } break; } } - // Draw secondary halos. - if(flareDrawn && haloMode > 1) + // Draw secondary halos? + if(primaryHaloDrawn && haloMode > 1) { // Now we can setup the state only once. H_SetupState(true); - for(vissprite_t* spr = visSprSortedHead.next; spr != &visSprSortedHead; spr = spr->next) + for(vissprite_t *spr = visSprSortedHead.next; spr != &visSprSortedHead; spr = spr->next) { if(spr->type == VSPR_FLARE) { - generateHaloForVisSprite(spr, false); + generateHaloForVisSprite(spr); } } diff --git a/doomsday/client/src/render/vissprite.cpp b/doomsday/client/src/render/vissprite.cpp index d3d64408ee..1cc18552d0 100644 --- a/doomsday/client/src/render/vissprite.cpp +++ b/doomsday/client/src/render/vissprite.cpp @@ -40,7 +40,7 @@ void R_ClearVisSprites() visSpriteP = visSprites; } -vissprite_t *R_NewVisSprite() +vissprite_t *R_NewVisSprite(visspritetype_t type) { vissprite_t *spr; @@ -55,6 +55,7 @@ vissprite_t *R_NewVisSprite() } zapPtr(spr); + spr->type = type; return spr; }