Skip to content

Commit

Permalink
WallEdge: Determine the (possibly smoothed) edge normal at prepare time
Browse files Browse the repository at this point in the history
  • Loading branch information
danij-deng committed May 23, 2013
1 parent 32df94e commit f0f3e7c
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 124 deletions.
4 changes: 3 additions & 1 deletion doomsday/client/include/render/walledge.h
Expand Up @@ -66,7 +66,7 @@ class WallEdge

public:
WallEdge(Line::Side &lineSide, int section, coord_t lineOffset,
Vertex &lineVertex, de::ClockDirection neighborScanDirection);
Vertex &lineVertex, int edge);

WallEdge(HEdge &hedge, int section, int edge);

Expand Down Expand Up @@ -111,6 +111,8 @@ class WallEdge

de::Vector2f const &materialOrigin() const;

de::Vector3f const &normal() const;

Intercepts const &intercepts() const;

private:
Expand Down
112 changes: 14 additions & 98 deletions doomsday/client/src/render/rend_main.cpp
Expand Up @@ -1240,122 +1240,38 @@ static float calcLightLevelDelta(Vector3f const &normal)
return (1.0f / 255) * (normal.x * 18) * rendLightWallAngle;
}

static Line::Side *findSideBlendNeighbor(Line::Side const &side, int edge, binangle_t *diff = 0)
{
LineOwner const *farVertOwner = side.line().vertexOwner(side.lineSideId() ^ edge);

if(diff) *diff = 0;

Line *neighbor;
if(R_SideBackClosed(side))
{
neighbor = R_FindSolidLineNeighbor(side.sectorPtr(), &side.line(), farVertOwner, edge, diff);
}
else
{
neighbor = R_FindLineNeighbor(side.sectorPtr(), &side.line(), farVertOwner, edge, diff);
}

// No suitable line neighbor?
if(!neighbor) return 0;

// Choose the correct side of the neighbor (determined by which vertex is shared).
Line::Side *otherSide;
if(&neighbor->vertex(edge ^ 1) == &side.vertex(edge))
otherSide = &neighbor->front();
else
otherSide = &neighbor->back();

// We can only blend neighbors with surface sections.
return otherSide->hasSections()? otherSide : 0;
}

/**
* Determines whether light level delta smoothing should be performed for
* the given pair of map surfaces (which are assumed to share an edge).
*
* Yes if the angle between the two lines is less than 45 degrees.
* @todo Should be user customizable with a Material property. -ds
*
* @param sufA The "left" map surface which shares an edge with @a sufB.
* @param sufB The "right" map surface which shares an edge with @a sufA.
* @param angleDiff Angle difference (i.e., normal delta) between the two surfaces.
*/
static bool shouldSmoothLightLevels(Surface &sufA, Surface &sufB, binangle_t angleDiff)
{
DENG_UNUSED(sufA);
DENG_UNUSED(sufB);
return INRANGE_OF(angleDiff, BANG_180, BANG_45);
}

/**
* The DOOM lighting model applies a sector light level delta when drawing
* walls based on their 2D world angle.
*
* @todo: Use the half-edge rings instead of LineOwners.
*
* @param side Line side to calculate light level deltas for.
* @param section Section of the side for which deltas are to be calculated.
* @param edge Edge of the side for which deltas are to be calculated.
*
* @return Light delta for the specified wall section edge.
*/
static float wallWallEdgeLightLevelDelta(Line::Side &side, int section, int edge)
static void wallSectionLightLevelDeltas(WallEdge const &leftEdge, WallEdge const &rightEdge,
float &leftDelta, float &rightDelta)
{
// Are light level deltas disabled?
if(rendLightWallAngle <= 0) return 0;
if(rendLightWallAngle <= 0)
{
leftDelta = rightDelta = 0;
return;
}

// ...always if the surface's material was chosen as a HOM fix (lighting
// must be consistent with that applied to the relative back sector plane).
Line::Side &side = leftEdge.lineSide();
if(side.hasSector() && side.back().hasSector() &&
side.surface(section).hasFixMaterial())
return 0;

float delta = calcLightLevelDelta(side.surface(section).normal());

// Is delta smoothing disabled?
if(!rendLightWallAngleSmooth) return delta;
// ...always for polyobj lines (no owner rings).
if(side.line().definesPolyobj()) return delta;

/**
* Smoothing is enabled, so find the neighbor side for the specified edge
* which we will use to calculate the averaged lightlevel delta for each.
*
* @todo Do not assume the neighbor is the middle section of @var otherSide.
*/
binangle_t angleDiff;
if(Line::Side *otherSide = findSideBlendNeighbor(side, edge, &angleDiff))
side.surface(leftEdge.section()).hasFixMaterial())
{
Surface &sufA = side.surface(section);
Surface &sufB = otherSide->surface(section);

if(shouldSmoothLightLevels(sufA, sufB, angleDiff))
{
// Average normals.
Vector3f avgNormal = Vector3f(sufA.normal() + sufB.normal()) / 2;
return calcLightLevelDelta(avgNormal);
}
leftDelta = rightDelta = 0;
return;
}

return delta;
}

/**
* Calculate the light level deltas for this wall section.
* @todo Cache these values somewhere. -ds
*/
static void wallSectionLightLevelDeltas(WallEdge const &leftEdge, WallEdge const &rightEdge,
float &leftDelta, float &rightDelta)
{
leftDelta = wallWallEdgeLightLevelDelta(leftEdge.lineSide(), leftEdge.section(), Line::From);
rightDelta = wallWallEdgeLightLevelDelta(rightEdge.lineSide(), rightEdge.section(), Line::To);
leftDelta = calcLightLevelDelta(leftEdge.normal());
rightDelta = calcLightLevelDelta(rightEdge.normal());

if(!de::fequal(leftDelta, rightDelta))
{
// Linearly interpolate to find the light level delta values for the
// vertical edges of this wall section.
coord_t const lineLength = leftEdge.lineSide().line().length();
coord_t const lineLength = side.line().length();
coord_t const sectionOffset = leftEdge.lineOffset();
coord_t const sectionWidth = de::abs(Vector2d(rightEdge.origin() - leftEdge.origin()).length());

Expand Down
139 changes: 114 additions & 25 deletions doomsday/client/src/render/walledge.cpp
Expand Up @@ -24,6 +24,8 @@
#include "map/r_world.h" // R_SideSectionCoords
#include "map/lineowner.h"

#include "render/rend_main.h" // rendLightWallAngleSmooth

#include "render/walledge.h"

using namespace de;
Expand Down Expand Up @@ -52,12 +54,13 @@ DENG2_PIMPL(WallEdge), public IHPlane
{
Line::Side *lineSide;
int section;
ClockDirection neighborScanDirection;
int edge;
coord_t lineOffset;
Vertex *lineVertex;

bool isValid;
Vector2f materialOrigin;
Vector3f edgeNormal;

/// Data for the IHPlane model.
struct HPlane
Expand All @@ -80,28 +83,27 @@ DENG2_PIMPL(WallEdge), public IHPlane
{}
} hplane;

Instance(Public *i, Line::Side *lineSide, int section,
ClockDirection neighborScanDirection,
Instance(Public *i, Line::Side *lineSide, int section, int edge,
coord_t lineOffset, Vertex *lineVertex)
: Base(i),
lineSide(lineSide),
section(section),
neighborScanDirection(neighborScanDirection),
edge(edge),
lineOffset(lineOffset),
lineVertex(lineVertex),
isValid(false)
{}

Instance(Public *i, Instance const &other)
: Base(i),
lineSide (other.lineSide),
section (other.section),
neighborScanDirection(other.neighborScanDirection),
lineOffset (other.lineOffset),
lineVertex (other.lineVertex),
isValid (other.isValid),
materialOrigin (other.materialOrigin),
hplane (other.hplane)
lineSide (other.lineSide),
section (other.section),
edge (other.edge),
lineOffset (other.lineOffset),
lineVertex (other.lineVertex),
isValid (other.isValid),
materialOrigin(other.materialOrigin),
hplane (other.hplane)
{}

void verifyValid() const
Expand Down Expand Up @@ -231,7 +233,7 @@ DENG2_PIMPL(WallEdge), public IHPlane
bool stopScan = false;
do
{
own = &own->navigate(neighborScanDirection);
own = &own->navigate(edge? Anticlockwise : Clockwise);

if(own == base)
{
Expand Down Expand Up @@ -320,17 +322,15 @@ DENG2_PIMPL(WallEdge), public IHPlane
Instance &operator = (Instance const &); // no assignment
};

WallEdge::WallEdge(Line::Side &lineSide, int section,
coord_t lineOffset, Vertex &lineVertex, ClockDirection neighborScanDirection)
: d(new Instance(this, &lineSide, section, neighborScanDirection,
lineOffset, &lineVertex))
WallEdge::WallEdge(Line::Side &lineSide, int section, coord_t lineOffset,
Vertex &lineVertex, int edge)
: d(new Instance(this, &lineSide, section, edge, lineOffset, &lineVertex))
{
DENG_ASSERT(lineSide.hasSections());
}

WallEdge::WallEdge(HEdge &hedge, int section, int edge)
: d(new Instance(this, &hedge.lineSide(), section,
edge? Anticlockwise : Clockwise,
: d(new Instance(this, &hedge.lineSide(), section, edge,
hedge.lineOffset() + (edge? hedge.length() : 0),
edge? &hedge.twin().vertex() : &hedge.vertex()))
{
Expand Down Expand Up @@ -400,10 +400,78 @@ int WallEdge::lastDivision() const
return d->interceptCount()-2;
}

Vector2f const &WallEdge::materialOrigin() const
WallEdge::Intercept const &WallEdge::at(int index) const
{
d->verifyValid();
return d->materialOrigin;
return d->hplane.intercepts[index];
}

WallEdge::Intercepts const &WallEdge::intercepts() const
{
d->verifyValid();
return d->hplane.intercepts;
}

/**
* Determines whether light level delta smoothing should be performed for
* the given pair of map surfaces (which are assumed to share an edge).
*
* Yes if the angle between the two lines is less than 45 degrees.
* @todo Should be user customizable with a Material property. -ds
*
* @param sufA The "left" map surface which shares an edge with @a sufB.
* @param sufB The "right" map surface which shares an edge with @a sufA.
* @param angleDiff Angle difference (i.e., normal delta) between the two surfaces.
*/
static bool shouldSmoothLightLevels(Surface &sufA, Surface &sufB, binangle_t angleDiff)
{
DENG_UNUSED(sufA);
DENG_UNUSED(sufB);
return INRANGE_OF(angleDiff, BANG_180, BANG_45);
}

/**
* Find the neighbor side for the specified edge which we will use to calculate
* the edge normal.
*
* @todo: Use the half-edge rings instead of LineOwners.
*/
static Line::Side *findSideBlendNeighbor(Line::Side const &side, int edge, binangle_t *diff = 0)
{
// Is smoothing disabled?
if(!rendLightWallAngleSmooth)
return 0;

// Polyobj lines have no owner rings.
if(side.line().definesPolyobj())
return 0;

LineOwner const *farVertOwner = side.line().vertexOwner(side.lineSideId() ^ edge);

if(diff) *diff = 0;

Line *neighbor;
if(R_SideBackClosed(side))
{
neighbor = R_FindSolidLineNeighbor(side.sectorPtr(), &side.line(), farVertOwner, edge, diff);
}
else
{
neighbor = R_FindLineNeighbor(side.sectorPtr(), &side.line(), farVertOwner, edge, diff);
}

// No suitable line neighbor?
if(!neighbor) return 0;

// Choose the correct side of the neighbor (determined by which vertex is shared).
Line::Side *otherSide;
if(&neighbor->vertex(edge ^ 1) == &side.vertex(edge))
otherSide = &neighbor->front();
else
otherSide = &neighbor->back();

// We can only blend neighbors with surface sections.
return otherSide->hasSections()? otherSide : 0;
}

void WallEdge::prepare()
Expand Down Expand Up @@ -441,16 +509,37 @@ void WallEdge::prepare()

// Sanity check.
d->assertInterceptsInRange(bottom, top);

// Determine the edge normal.
binangle_t angleDiff;
if(Line::Side *otherSide = findSideBlendNeighbor(*d->lineSide, d->edge, &angleDiff))
{
/// @todo Do not assume the neighbor is the middle section of @var otherSide.
/// @todo Cache the smoothed normal value somewhere. -ds

Surface &sufA = d->lineSide->surface(d->section);
Surface &sufB = otherSide->surface(d->section);

if(shouldSmoothLightLevels(sufA, sufB, angleDiff))
{
// Average normals.
d->edgeNormal = Vector3f(sufA.normal() + sufB.normal()) / 2;
}
}
else
{
d->edgeNormal = d->lineSide->surface(d->section).normal();
}
}

WallEdge::Intercept const &WallEdge::at(int index) const
Vector2f const &WallEdge::materialOrigin() const
{
d->verifyValid();
return d->hplane.intercepts[index];
return d->materialOrigin;
}

WallEdge::Intercepts const &WallEdge::intercepts() const
Vector3f const &WallEdge::normal() const
{
d->verifyValid();
return d->hplane.intercepts;
return d->edgeNormal;
}

0 comments on commit f0f3e7c

Please sign in to comment.