forked from Mirrors/openclonk
Reworked CalculateTriangles, fixed descend collision
Descend collision was broken because of two reasons - one complicated and one easy. Firstly, we would not re-visit beams after eliminations if they were closer to the light source than the remaining beam. Especially nasty because the comments claimed the opposite. Secondly, the coordinates passed to find_cross were actually flipped. I took the opportunity to clean up the control structure a bit, update the comments (brr), and fix the bug in one swoop.alternate-lights
parent
52a2f4bb8e
commit
d976a4b2a7
|
@ -468,8 +468,11 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
float scanLevel = 0;
|
||||
for (int step = 0; step < 100000; step++)
|
||||
{
|
||||
// Find the beam to project. This makes this whole algorithm O(n²),
|
||||
// but I see no other way to make the whole thing robust :/
|
||||
// Find the beam to project. The whole idea here is that we reduce the
|
||||
// beams starting with the closest ones. Reason for this is that they
|
||||
// generally shadow the most, and this makes it easy to make the
|
||||
// algorithm robust against light size depending on distance. Sadly
|
||||
// makes the whole algorithm O(n^2)...
|
||||
float bestLevel = FLT_MAX;
|
||||
for (std::list<C4FoWBeamTriangle>::iterator it = result.begin(), nextIt; it != --result.end(); ++it)
|
||||
{
|
||||
|
@ -483,11 +486,16 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
break;
|
||||
scanLevel = bestLevel;
|
||||
|
||||
// Now look for all beams at this length. Will propably be only one
|
||||
// most of the time, but can't be too careful. Especially note that
|
||||
// we will make extra loops after removing rays, so we can check the
|
||||
// new neighbouring relation for consistency.
|
||||
for(std::list<C4FoWBeamTriangle>::iterator it = result.begin(), nextIt; it != --result.end(); it = nextIt)
|
||||
{
|
||||
nextIt = it; ++nextIt;
|
||||
C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt;
|
||||
C4FoWBeamTriangle tri = *it, nextTri = *nextIt;
|
||||
|
||||
// Skip ray pairs that do not match the current level (see above)
|
||||
float level = Min(tri.fanRY, nextTri.fanLY);
|
||||
if(level != bestLevel)
|
||||
continue;
|
||||
|
@ -535,6 +543,7 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
assert(tri.fanRY <= tri.fanLY);
|
||||
tri.fanLX = tri.fanRX;
|
||||
tri.fanLY = tri.fanRY;
|
||||
*it = tri;
|
||||
}
|
||||
|
||||
// The threshold decides at what point we are going to eliminate
|
||||
|
@ -559,7 +568,6 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
}
|
||||
|
||||
// Move right point of left beam to the left (the original point is partly shadowed)
|
||||
bool eliminate = false;
|
||||
float b;
|
||||
bool f = find_cross(lightRX, lightRY, nextTri.fanLX, nextTri.fanLY,
|
||||
tri.fanLX, tri.fanLY, fanRXp, tri.fanRY,
|
||||
|
@ -576,7 +584,31 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
// shadowed, and we don't need to draw it.
|
||||
if (b >= threshold)
|
||||
{
|
||||
eliminate = true;
|
||||
// Can't eliminate it?
|
||||
if (it == result.begin())
|
||||
continue;
|
||||
|
||||
// Remove the beam.
|
||||
it = nextIt = result.erase(it);
|
||||
tri = *--it;
|
||||
// Now decide how to proceed: If the new previous ray (it)
|
||||
// is farther away, we have to repeat this whole check
|
||||
// because this one (nextIt) might shadow it as well.
|
||||
if (tri.fanRY > nextTri.fanLY)
|
||||
{
|
||||
nextIt = it;
|
||||
continue;
|
||||
}
|
||||
|
||||
// However if the previous one is *closer*, this means we
|
||||
// cannot possible shadow it. Furthermore, we know that
|
||||
// it cannot shadow this one either. The fact that there was
|
||||
// a beam between the two means that there is now a "hole"
|
||||
// that needs to be filled. Hence we have a descend
|
||||
// collision. We could get there by looping like above,
|
||||
// but this is more elegant.
|
||||
descendCollision = true;
|
||||
LightBallLeftMostPoint(tri.fanLX, tri.fanLY, lightLX, lightLY);
|
||||
|
||||
// Cross point actually right of surface? This can happen when
|
||||
// we eliminated surfaces. It means that the light doesn't reach
|
||||
|
@ -597,23 +629,10 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
// Set cross point
|
||||
tri.fanRX = crossX;
|
||||
tri.fanRY = crossY;
|
||||
}
|
||||
|
||||
// This shouldn't change the case we are in (uh, I think)
|
||||
assert(tri.fanRY > nextTri.fanLY);
|
||||
|
||||
// Did we eliminate the surface with this step?
|
||||
if (eliminate && it != result.begin())
|
||||
{
|
||||
// With the elimination, we need to re-process the last
|
||||
// beam, as it might be more shadowed than we realized.
|
||||
// Note that the last point might have been projected already -
|
||||
// but that's okay
|
||||
nextIt = it; nextIt--;
|
||||
|
||||
// Remove it (note that this invalidates "it". The iterator,
|
||||
// that is.)
|
||||
result.erase(it);
|
||||
// This shouldn't change the case we are in (uh, I think)
|
||||
assert(tri.fanRY > nextTri.fanLY);
|
||||
// Write back
|
||||
*it = tri;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -637,6 +656,7 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
assert(nextTri.fanLY <= nextTri.fanRY);
|
||||
nextTri.fanRX = nextTri.fanLX;
|
||||
nextTri.fanRY = nextTri.fanLY;
|
||||
*nextIt = nextTri;
|
||||
}
|
||||
float fanRXp = nextTri.fanRX;
|
||||
float threshold = 0.0f;
|
||||
|
@ -645,7 +665,6 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
if (nextTri.fanRX == nextTri.fanLX && nextTri.fanRY == nextTri.fanLY)
|
||||
fanRXp += 1.0;
|
||||
|
||||
bool eliminate = false;
|
||||
float b;
|
||||
bool f = find_cross(lightLX, lightLY, tri.fanRX, tri.fanRY,
|
||||
nextTri.fanLX, nextTri.fanLY, fanRXp, nextTri.fanRY,
|
||||
|
@ -656,7 +675,17 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
assert(f);
|
||||
if (b <= threshold)
|
||||
{
|
||||
eliminate = true;
|
||||
if (nextIt == --result.end())
|
||||
continue;
|
||||
nextIt = result.erase(nextIt);
|
||||
nextTri = *nextIt;
|
||||
if (nextTri.fanLY > tri.fanRY)
|
||||
{
|
||||
nextIt = it;
|
||||
continue;
|
||||
}
|
||||
descendCollision = true;
|
||||
LightBallRightMostPoint(nextTri.fanLX, nextTri.fanLY, lightRX, lightRY);
|
||||
}
|
||||
else if (b > 1.0)
|
||||
{
|
||||
|
@ -666,56 +695,72 @@ std::list<C4FoWBeamTriangle> C4FoWLightSection::CalculateTriangles(C4FoWRegion *
|
|||
{
|
||||
nextTri.fanLX = crossX;
|
||||
nextTri.fanLY = crossY;
|
||||
}
|
||||
assert(tri.fanRY < nextTri.fanLY);
|
||||
if (eliminate && nextIt != --result.end())
|
||||
{
|
||||
// We remove the next triangle, so re-process this one.
|
||||
nextIt = it;
|
||||
it++;
|
||||
result.erase(it);
|
||||
assert(tri.fanRY < nextTri.fanLY);
|
||||
*nextIt = nextTri;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else { // tri.fanRY == nextTri.fanLY
|
||||
|
||||
// Check whether we have a significant gap
|
||||
if (nextTri.fanLX - tri.fanRX > 0.5) {
|
||||
descendCollision = true;
|
||||
} else {
|
||||
// Nothing to do
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (descendCollision)
|
||||
{
|
||||
// Should never be parallel -- otherwise we wouldn't be here
|
||||
// in the first place.
|
||||
bool f = find_cross(lightRX, lightRY, tri.fanRX, tri.fanRY,
|
||||
lightLX, lightLY, nextTri.fanLX, nextTri.fanLY,
|
||||
&crossX, &crossY);
|
||||
assert(f);
|
||||
// We should only reach this place with a descend collision
|
||||
assert(descendCollision);
|
||||
|
||||
// Ensure some minimum distance to existing points - don't
|
||||
// bother with too small bumps. This also catches some floating
|
||||
// point inacurracies.
|
||||
const float descendEta = 0.5;
|
||||
if (crossY <= tri.fanRY + descendEta || crossY <= nextTri.fanLY + descendEta)
|
||||
continue;
|
||||
// Should never be parallel -- otherwise we wouldn't be here
|
||||
// in the first place.
|
||||
bool f = find_cross(lightLX, lightLY, tri.fanRX, tri.fanRY,
|
||||
lightRX, lightRY, nextTri.fanLX, nextTri.fanLY,
|
||||
&crossX, &crossY);
|
||||
assert(f);
|
||||
#ifdef FAN_STEP_DEBUG
|
||||
LogSilentF("Collision, cross=%.02f/%.02f", crossX, crossY);
|
||||
#endif
|
||||
|
||||
// This should always follow an elimination, but better check
|
||||
assert(beamCount > result.size());
|
||||
// Ensure some minimum distance to existing points - don't
|
||||
// bother with too small bumps. This also catches some floating
|
||||
// point inacurracies.
|
||||
const float descendEta = 3;
|
||||
if (crossY <= tri.fanRY + descendEta || crossY <= nextTri.fanLY + descendEta)
|
||||
continue;
|
||||
|
||||
C4FoWBeamTriangle newTriangle;
|
||||
newTriangle.fanLX = crossX;
|
||||
newTriangle.fanLY = crossY;
|
||||
newTriangle.fanRX = crossX;
|
||||
newTriangle.fanRY = crossY;
|
||||
// This should always follow an elimination, but better check
|
||||
assert(beamCount > result.size());
|
||||
|
||||
nextIt = result.insert(nextIt, newTriangle);
|
||||
C4FoWBeamTriangle newTriangle;
|
||||
newTriangle.fanLX = crossX;
|
||||
newTriangle.fanLY = crossY;
|
||||
newTriangle.fanRX = crossX;
|
||||
newTriangle.fanRY = crossY;
|
||||
|
||||
// Jump over surface. Note that our right beam might get
|
||||
// eliminated later on, causing us to back-track into this
|
||||
// zero-length pseudo-surface. This will cause find_cross
|
||||
// above to eliminate the pseudo-surface and back-track
|
||||
// further to the left, which is exactly how it should work.
|
||||
++nextIt;
|
||||
}
|
||||
nextIt = result.insert(nextIt, newTriangle);
|
||||
|
||||
// Jump over surface. Note that our right beam might get
|
||||
// eliminated later on, causing us to back-track into this
|
||||
// zero-length pseudo-surface. This will cause find_cross
|
||||
// above to eliminate the pseudo-surface and back-track
|
||||
// further to the left, which is exactly how it should work.
|
||||
++nextIt;
|
||||
|
||||
} // end for(std::list<C4FoWBeamTriangle>::iterator it = result.begin(), nextIt = it; it != --result.end(); ++it) loop
|
||||
} // end for (int step = 0; step < 100000; step++) loop
|
||||
|
||||
#ifdef FAN_STEP_DEBUG
|
||||
LogSilentF("Fan output");
|
||||
for (std::list<C4FoWBeamTriangle>::iterator it2 = result.begin(); it2 != result.end(); it2++) {
|
||||
LogSilentF(" %.010f %.010f", it2->fanLX, it2->fanLY);
|
||||
LogSilentF(" %.010f %.010f", it2->fanRX, it2->fanRY);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Phase 2: Calculate fade points
|
||||
for (std::list<C4FoWBeamTriangle>::iterator it = result.begin(); it != result.end(); ++it)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue