rename C4FoWRay to C4FoWBeam, add some documentation

issue1247
Tobias Zwick 2014-10-12 12:59:25 +02:00
parent d859789d96
commit 7963065eb8
9 changed files with 330 additions and 319 deletions

View File

@ -288,8 +288,8 @@ set(OC_CLONK_SOURCES
src/gui/C4UpperBoard.h src/gui/C4UpperBoard.h
src/landscape/fow/C4FoW.cpp src/landscape/fow/C4FoW.cpp
src/landscape/fow/C4FoW.h src/landscape/fow/C4FoW.h
src/landscape/fow/C4FoWRay.cpp src/landscape/fow/C4FoWBeam.cpp
src/landscape/fow/C4FoWRay.h src/landscape/fow/C4FoWBeam.h
src/landscape/fow/C4FoWLight.cpp src/landscape/fow/C4FoWLight.cpp
src/landscape/fow/C4FoWLight.h src/landscape/fow/C4FoWLight.h
src/landscape/fow/C4FoWLightSection.cpp src/landscape/fow/C4FoWLightSection.cpp

View File

@ -29,10 +29,10 @@ public:
/** Removes the light associated with the given object, if any */ /** Removes the light associated with the given object, if any */
void Remove(C4Object *pObj); void Remove(C4Object *pObj);
/** Update all light rays within the given rectangle */ /** Update all light beams within the given rectangle */
// ASK: only called by C4FoWRegion?? // ASK: only called by C4FoWRegion??
void Update(C4Rect r); void Update(C4Rect r);
/** Triggers the recalculation of all light rays within the given rectangle because the landscape changed. */ /** Triggers the recalculation of all light beams within the given rectangle because the landscape changed. */
void Invalidate(C4Rect r); void Invalidate(C4Rect r);
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL); void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);

View File

@ -1,8 +1,8 @@
#include "C4Include.h" #include "C4Include.h"
#include "C4FoWRay.h" #include "C4FoWBeam.h"
// Maximum error allowed while merging rays. // Maximum error allowed while merging beams.
const int32_t C4FoWMergeThreshold = 10; // (in landscape pixels * 2) const int32_t C4FoWMergeThreshold = 10; // (in landscape pixels * 2)
// A = 1/2 | a x b | // A = 1/2 | a x b |
@ -16,7 +16,7 @@ static inline int32_t getDoubleTriangleSurface(int32_t x1, int32_t y1, int32_t x
return abs(ax * by - ay * bx); return abs(ax * by - ay * bx);
} }
StdStrBuf C4FoWRay::getDesc() const { StdStrBuf C4FoWBeam::getDesc() const {
return FormatString("%d:%d@%d:%d%s", return FormatString("%d:%d@%d:%d%s",
getLeftX(1000), getLeftX(1000),
getRightX(1000), getRightX(1000),
@ -25,7 +25,7 @@ StdStrBuf C4FoWRay::getDesc() const {
fDirty ? "*" : ""); fDirty ? "*" : "");
} }
bool C4FoWRay::MergeRight(int x, int y) bool C4FoWBeam::MergeRight(int x, int y)
{ {
// Note: Right-merging is the most common and most important optimization. // Note: Right-merging is the most common and most important optimization.
// This procedure will probably be *hammered* as a result. Worth inlining? // This procedure will probably be *hammered* as a result. Worth inlining?
@ -50,7 +50,7 @@ bool C4FoWRay::MergeRight(int x, int y)
return true; return true;
} }
bool C4FoWRay::MergeLeft(int x, int y) bool C4FoWBeam::MergeLeft(int x, int y)
{ {
assert(!isDirty()); assert(isLeft(x, y)); assert(!isDirty()); assert(isLeft(x, y));
@ -70,14 +70,14 @@ bool C4FoWRay::MergeLeft(int x, int y)
return true; return true;
} }
bool C4FoWRay::Eliminate(int x, int y) bool C4FoWBeam::Eliminate(int x, int y)
{ {
// Called on the ray left of the one getting eliminated // Called on the beams left of the one getting eliminated
C4FoWRay *pElim = pNext, *pMerge = pNext->pNext; C4FoWBeam *pElim = pNext, *pMerge = pNext->pNext;
assert(!!pElim); assert(!!pMerge); assert(!!pElim); assert(!!pMerge);
assert(!isDirty()); assert(!pMerge->isDirty()); assert(!isDirty()); assert(!pMerge->isDirty());
// Calc errors, add those accumulated on both merged rays // Calc errors, add those accumulated on both merged beams
int32_t iErr = getDoubleTriangleSurface( int32_t iErr = getDoubleTriangleSurface(
getLeftEndX(), iLeftEndY, getLeftEndX(), iLeftEndY,
pMerge->getRightEndX(), pMerge->iLeftEndY, pMerge->getRightEndX(), pMerge->iLeftEndY,
@ -96,34 +96,34 @@ bool C4FoWRay::Eliminate(int x, int y)
return true; return true;
} }
C4FoWRay *C4FoWRay::Split(int x, int y) C4FoWBeam *C4FoWBeam::Split(int x, int y)
{ {
// Make sure to never create negative-surface rays // Make sure to never create negative-surface beams
assert(isDirty()); assert(isInside(x, y)); assert(isDirty()); assert(isInside(x, y));
// Allocate a new ray. Ugh, expensive. // Allocate a new beam. Ugh, expensive.
C4FoWRay *pRay = new C4FoWRay(x, y, iRightX, iRightY); C4FoWBeam *pBeam = new C4FoWBeam(x, y, iRightX, iRightY);
pRay->Dirty(iLeftEndY); pBeam->Dirty(iLeftEndY);
// Move to make space // Move to make space
iRightX = x; iRightX = x;
iRightY = y; iRightY = y;
// Relink // Relink
pRay->pNext = pNext; pBeam->pNext = pNext;
pNext = pRay; pNext = pBeam;
return pRay; return pBeam;
} }
void C4FoWRay::MergeDirty() void C4FoWBeam::MergeDirty()
{ {
// As a rule, dirty rays following each other should // As a rule, dirty beams following each other should
// always be merged, so splits can be reverted once // always be merged, so splits can be reverted once
// the landscape changes. // the landscape changes.
C4FoWRay *pWith = pNext; C4FoWBeam *pWith = pNext;
assert(isDirty()); assert(!!pWith); assert(pWith->isDirty()); assert(isDirty()); assert(!!pWith); assert(pWith->isDirty());
// Figure out how far the new dirty ray reaches. Note that // Figure out how far the new dirty beams reaches. Note that
// we might lose information about the landscape here. // we might lose information about the landscape here.
Dirty(Min(getLeftEndY(), pWith->getLeftEndY())); Dirty(Min(getLeftEndY(), pWith->getLeftEndY()));
@ -136,30 +136,30 @@ void C4FoWRay::MergeDirty()
delete pWith; delete pWith;
} }
void C4FoWRay::Clean(int y) void C4FoWBeam::Clean(int y)
{ {
// Search hit something, this ray is now clean. // Search hit something, this beam is now clean.
assert(isDirty()); assert(isDirty());
iLeftEndY = y; iLeftEndY = y;
iRightEndY = y; iRightEndY = y;
fDirty = false; fDirty = false;
} }
void C4FoWRay::Dirty(int y) void C4FoWBeam::Dirty(int y)
{ {
// Got invalidated, ray is dirty until updated // Got invalidated, beam is dirty until updated
iLeftEndY = y; iLeftEndY = y;
iRightEndY = y; iRightEndY = y;
fDirty = true; fDirty = true;
} }
void C4FoWRay::Prune(int32_t y) void C4FoWBeam::Prune(int32_t y)
{ {
// Check which sides we need to prune // Check which sides we need to prune
bool fLeft = (iLeftEndY >= y), bool fLeft = (iLeftEndY >= y),
fRight = (iRightEndY >= y); fRight = (iRightEndY >= y);
// If both sides got pruned, we are clean // If both sides got pruned, we are clean
// (can't possibly extend this ray further) // (can't possibly extend this beam further)
if (fLeft && fRight) if (fLeft && fRight)
Clean(y); Clean(y);
else if (fLeft) else if (fLeft)

View File

@ -0,0 +1,109 @@
#ifndef C4FOWBEAM_H
#define C4FOWBEAM_H
#include "StdBuf.h"
/** This class represents one beam. A beam is a triangle spanned by two line segment: one going from the origin to the
left delimiter point, one going from the origin to the right delimiter point.
Other than this, a beam keeps the position at which it hit solid material, each for the left and for the right
line segment.
A beam can be marked as dirty. This means... TODO
*/
class C4FoWBeam
{
public:
C4FoWBeam(int32_t iLeftX, int32_t iLeftY, int32_t iRightX, int32_t iRightY)
: iLeftX(iLeftX), iLeftY(iLeftY), iRightX(iRightX), iRightY(iRightY),
iLeftEndY(0), iRightEndY(0),
iError(0),
fDirty(true),
pNext(NULL)
{ }
private:
int32_t iLeftX, iLeftY; // left delimiter point
int32_t iRightX, iRightY; // right delimiter point
int32_t iLeftEndY, iRightEndY; // where it hit solid material.
int32_t iError; // How much error this beam has
bool fDirty; // landscape changed since it was followed?
C4FoWBeam *pNext;
public:
bool isDirty() const { return fDirty; }
bool isClean() const { return !fDirty; }
C4FoWBeam *getNext() const { return pNext; }
// Get a point on the beam boundary.
inline int32_t getLeftX(int32_t y) const { return iLeftX * y / iLeftY; }
inline int32_t getRightX(int32_t y) const { return iRightX * y / iRightY; }
inline float getLeftXf(int32_t y) const { return (iLeftX * y) / float(iLeftY); }
inline float getRightXf(int32_t y) const { return (iRightX * y) / float(iRightY); }
int32_t getLeftEndY() const { return iLeftEndY; }
int32_t getLeftEndX() const { return getLeftX(iLeftEndY); }
float getLeftEndXf() const { return getLeftXf(iLeftEndY); }
int32_t getRightEndY() const { return iRightEndY; }
int32_t getRightEndX() const { return getRightX(iRightEndY); }
float getRightEndXf() const { return getRightXf(iRightEndY); }
StdStrBuf getDesc() const;
/** returns whether the given point is left of an imaginery line drawn from the left delimiter point to the origin (point is left of beam) */
bool isLeft(int x, int y) const {
return iLeftX * y > x * iLeftY;
}
/** returns whether the given point is right of an imaginery line drawn from the right delimiter point to the origin (point is right of beam) */
bool isRight(int x, int y) const {
return iRightX * y < x * iRightY;
}
/** returns whether the given point is inside the triangle that is defined by this beam */
bool isInside(int x, int y) const {
return !isLeft(x, y) && !isRight(x, y);
}
void SetLeft(int x, int y) { iLeftX = x; iLeftY = y; }
void SetRight(int x, int y) { iRightX = x; iRightY = y; }
/** Set the new right delimiter point to the given point if the resulting difference in size is less than the error
threshold. If successfull, adds the introduced error to iError.
Asserts that the given point is really right of the right delimiter point
*/
// ASK: this procedure must certainly involve the beam right of this one, the code for this is probably in C4FoWLightSection? If yes, this should probably be managed by this class? (as it already manages things like that in Split and Eliminate)
bool MergeRight(int x, int y);
/** Set the new left delimiter point to the given point if the resulting difference in size is less than the error
threshold. If successfull, adds the introduced error to iError.
Asserts that the given point is really left of the right delimiter point
*/
bool MergeLeft(int x, int y);
/** Split this beam into two: this beam and the returned one. The given point x,y is the position at
which the two resulting beams are connected with their left/right endpoints.
It is asserted that the given point is inside the previous beam. (So the beam can only made smaller) */
// ASK: is it intended that the beam can only be made smaller by splitting?
C4FoWBeam *Split(int x, int y);
/** Makes this beam span from its left delimiter point to the right delimiter point of the next but one beam,
removing the two beams in between in the process.
The position x,y is only used for the error calcualation and not actually inserted.
Returns false and does not do the action in case the error threshold would be reached with these parameters
*/
// ASK: iError is not added to the error counter of this beam on success
// ASK: the x,y coordinate given is never actually added - even though it counts towards the error counter
bool Eliminate(int x, int y);
void MergeDirty();
void Clean(int32_t y);
void Dirty(int32_t y);
void Prune(int32_t y);
// TODO: find out more about this DIRTY stuff
};
#endif // C4FOWBEAM

View File

@ -36,7 +36,7 @@ void C4FoWLight::SetReach(int32_t iReach2, int32_t iFadeout2)
if (iReach == iReach2) return; if (iReach == iReach2) return;
// Reach decreased? Prune rays // Reach decreased? Prune beams
if (iReach2 < iReach) { if (iReach2 < iReach) {
iReach = iReach2; iReach = iReach2;
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
@ -44,7 +44,7 @@ void C4FoWLight::SetReach(int32_t iReach2, int32_t iFadeout2)
} else { } else {
// Reach increased? Dirty rays that might get longer now // Reach increased? Dirty beams that might get longer now
iReach = iReach2; iReach = iReach2;
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
pSect->Dirty(iReach); pSect->Dirty(iReach);
@ -59,7 +59,7 @@ void C4FoWLight::Update(C4Rect Rec)
if (iNX != iX || iNY != iY) if (iNX != iX || iNY != iY)
{ {
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
// pruning to zero length results in the rays being cleared and new ones created // pruning to zero length results in the beam being cleared and new ones created
pSect->Prune(0); pSect->Prune(0);
iX = iNX; iY = iNY; iX = iNX; iY = iNY;
} }

View File

@ -7,7 +7,7 @@
#include "C4FacetEx.h" #include "C4FacetEx.h"
/** This class represents one light source. A light source has an associated object with which the light source moves /** This class represents one light source. A light source has an associated object with which the light source moves
and one light section that handles the light rays for each direction (up, down, left, right). and one light section that handles the light beams for each direction (up, down, left, right).
Furthermore, each light source has a size. This is usually the object's PlrViewRange. See SetReach. Furthermore, each light source has a size. This is usually the object's PlrViewRange. See SetReach.
*/ */
@ -20,8 +20,8 @@ public:
private: private:
int32_t iX, iY; // center position int32_t iX, iY; // center position
int32_t iReach; // maximum length of rays int32_t iReach; // maximum length of beams
int32_t iFadeout; // number of pixels over which rays fade out int32_t iFadeout; // number of pixels over which beams fade out
int32_t iSize; // size of the light source. Decides smoothness of shadows int32_t iSize; // size of the light source. Decides smoothness of shadows
class C4FoWLightSection *pSections; class C4FoWLightSection *pSections;
C4FoWLight *pNext; C4FoWLight *pNext;
@ -32,7 +32,7 @@ public:
int32_t getY() const { return iY; } int32_t getY() const { return iY; }
int32_t getReach() const { return iReach; } int32_t getReach() const { return iReach; }
int32_t getFadeout() const { return iFadeout; } int32_t getFadeout() const { return iFadeout; }
// ASK: the code suggests taht total reach is iReach and iFadeout is subtracted from it // ASK: the code suggests taht total reach is iReach and iFadeout is subtracted from it, rather than added
int32_t getTotalReach() const { return iReach + iFadeout; } int32_t getTotalReach() const { return iReach + iFadeout; }
int32_t getSize() const { return iSize; } int32_t getSize() const { return iSize; }
C4FoWLight *getNext() const { return pNext; } C4FoWLight *getNext() const { return pNext; }
@ -42,9 +42,9 @@ public:
pixels after which the light should dim down */ pixels after which the light should dim down */
void SetReach(int32_t iReach, int32_t iFadeout); void SetReach(int32_t iReach, int32_t iFadeout);
/** Triggers the recalculation of all light rays within the given rectangle for this light because the landscape changed. */ /** Triggers the recalculation of all light beams within the given rectangle for this light because the landscape changed. */
void Invalidate(C4Rect r); void Invalidate(C4Rect r);
/** Update all light rays within the given rectangle for this light */ /** Update all light beams within the given rectangle for this light */
void Update(C4Rect r); void Update(C4Rect r);
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL); void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);

View File

@ -1,7 +1,7 @@
#include "C4Include.h" #include "C4Include.h"
#include "C4FoWLightSection.h" #include "C4FoWLightSection.h"
#include "C4FoWRay.h" #include "C4FoWBeam.h"
#include "C4FoWLight.h" #include "C4FoWLight.h"
#include "C4FoWRegion.h" #include "C4FoWRegion.h"
#include "C4Landscape.h" #include "C4Landscape.h"
@ -72,61 +72,61 @@ C4FoWLightSection::C4FoWLightSection(C4FoWLight *pLight, int r, C4FoWLightSectio
ra = 0; rb = 1; rc = -1; rd = 0; ra = 0; rb = 1; rc = -1; rd = 0;
break; break;
} }
// Ray list // Beam list
pRays = new C4FoWRay(-1, +1, +1, +1); pBeams = new C4FoWBeam(-1, +1, +1, +1);
} }
C4FoWLightSection::~C4FoWLightSection() C4FoWLightSection::~C4FoWLightSection()
{ {
ClearRays(); ClearBeams();
} }
void C4FoWLightSection::ClearRays() void C4FoWLightSection::ClearBeams()
{ {
while (C4FoWRay *pRay = pRays) { while (C4FoWBeam *pBeam = pBeams) {
pRays = pRay->getNext(); pBeams = pBeam->getNext();
delete pRay; delete pBeam;
} }
} }
void C4FoWLightSection::Prune(int32_t iReach) void C4FoWLightSection::Prune(int32_t iReach)
{ {
if (iReach == 0) { if (iReach == 0) {
ClearRays(); ClearBeams();
pRays = new C4FoWRay(-1, 1, 1, 1); pBeams = new C4FoWBeam(-1, 1, 1, 1);
return; return;
} }
// TODO: Merge active rays that we have pruned to same length // TODO: Merge active beams that we have pruned to same length
for (C4FoWRay *pRay = pRays; pRay; pRay = pRay->getNext()) for (C4FoWBeam *pBeam = pBeams; pBeam; pBeam = pBeam->getNext())
pRay->Prune(iReach); pBeam->Prune(iReach);
} }
void C4FoWLightSection::Dirty(int32_t iReach) void C4FoWLightSection::Dirty(int32_t iReach)
{ {
for (C4FoWRay *pRay = pRays; pRay; pRay = pRay->getNext()) for (C4FoWBeam *pBeam = pBeams; pBeam; pBeam = pBeam->getNext())
if (pRay->getLeftEndY() >= iReach || pRay->getRightEndY() >= iReach) if (pBeam->getLeftEndY() >= iReach || pBeam->getRightEndY() >= iReach)
pRay->Dirty(Min(pRay->getLeftEndY(), pRay->getRightEndY())); pBeam->Dirty(Min(pBeam->getLeftEndY(), pBeam->getRightEndY()));
} }
C4FoWRay *C4FoWLightSection::FindRayLeftOf(int32_t x, int32_t y) C4FoWBeam *C4FoWLightSection::FindBeamLeftOf(int32_t x, int32_t y)
{ {
// Trivial // Trivial
y = Max(y, 0); y = Max(y, 0);
if (!pRays || !pRays->isRight(x, y)) if (!pBeams || !pBeams->isRight(x, y))
return NULL; return NULL;
// Go through list // Go through list
// Note: In case this turns out expensive, one might think about implementing // Note: In case this turns out expensive, one might think about implementing
// a skip-list. But I highly doubt it. // a skip-list. But I highly doubt it.
C4FoWRay *pRay = pRays; C4FoWBeam *pBeam = pBeams;
while (pRay->getNext() && pRay->getNext()->isRight(x, y)) while (pBeam->getNext() && pBeam->getNext()->isRight(x, y))
pRay = pRay->getNext(); pBeam = pBeam->getNext();
return pRay; return pBeam;
} }
C4FoWRay *C4FoWLightSection::FindRayOver(int32_t x, int32_t y) C4FoWBeam *C4FoWLightSection::FindBeamOver(int32_t x, int32_t y)
{ {
C4FoWRay *pPrev = FindRayLeftOf(x, y); C4FoWBeam *pPrev = FindBeamLeftOf(x, y);
return pPrev ? pPrev->getNext() : pRays; return pPrev ? pPrev->getNext() : pBeams;
} }
void C4FoWLightSection::Update(C4Rect RectIn) void C4FoWLightSection::Update(C4Rect RectIn)
@ -138,13 +138,13 @@ void C4FoWLightSection::Update(C4Rect RectIn)
#ifdef LIGHT_DEBUG #ifdef LIGHT_DEBUG
if (!::Game.iTick255) { if (!::Game.iTick255) {
LogSilentF("Full ray list:"); LogSilentF("Full beam list:");
StdStrBuf Rays; StdStrBuf Beams;
for(C4FoWRay *pRay = pRays; pRay; pRay = pRay->getNext()) { for(C4FoWBeam *pBeam = pBeams; pBeam; pBeam = pBeam->getNext()) {
Rays.AppendChar(' '); Beams.AppendChar(' ');
Rays.Append(pRay->getDesc()); Beams.Append(pBeam->getDesc());
} }
LogSilent(Rays.getData()); LogSilent(Beams.getData());
} }
#endif #endif
@ -156,30 +156,30 @@ void C4FoWLightSection::Update(C4Rect RectIn)
if (Rect.y > pLight->getTotalReach()) if (Rect.y > pLight->getTotalReach())
return; return;
// Get last ray that's positively *not* affected // Get last beam that's positively *not* affected
int iLY = Max(0, RectLeftMostY(Rect)), int iLY = Max(0, RectLeftMostY(Rect)),
iRX = Rect.x+Rect.Wdt, iRY = Max(0, RectRightMostY(Rect)); iRX = Rect.x+Rect.Wdt, iRY = Max(0, RectRightMostY(Rect));
C4FoWRay *pStart = FindRayLeftOf(Rect.x, iLY); C4FoWBeam *pStart = FindBeamLeftOf(Rect.x, iLY);
// Skip clean rays // Skip clean beams
while (C4FoWRay *pNext = pStart ? pStart->getNext() : pRays) { while (C4FoWBeam *pNext = pStart ? pStart->getNext() : pBeams) {
if (pNext->isDirty()) break; if (pNext->isDirty()) break;
pStart = pNext; pStart = pNext;
} }
// Find end ray, determine at which position we have to start scanning // Find end beam, determine at which position we have to start scanning
C4FoWRay *pRay = pStart ? pStart->getNext() : pRays; C4FoWBeam *pBeam = pStart ? pStart->getNext() : pBeams;
#ifdef LIGHT_DEBUG #ifdef LIGHT_DEBUG
if (pRay) if (pBeam)
LogSilentF("Start ray is %s", pRay->getDesc().getData()); LogSilentF("Start beam is %s", pBeam->getDesc().getData());
#endif #endif
C4FoWRay *pEnd = NULL; C4FoWBeam *pEnd = NULL;
int32_t iStartY = Rect.GetBottom(); int32_t iStartY = Rect.GetBottom();
while (pRay && !pRay->isLeft(Rect.x+Rect.Wdt, iRY)) { while (pBeam && !pBeam->isLeft(Rect.x+Rect.Wdt, iRY)) {
if (pRay->isDirty() && pRay->getLeftEndY() <= Rect.y+Rect.Hgt) { if (pBeam->isDirty() && pBeam->getLeftEndY() <= Rect.y+Rect.Hgt) {
pEnd = pRay; pEnd = pBeam;
iStartY = Min(iStartY, pRay->getLeftEndY()); iStartY = Min(iStartY, pBeam->getLeftEndY());
} }
pRay = pRay->getNext(); pBeam = pBeam->getNext();
} }
// Can skip scan completely? // Can skip scan completely?
@ -188,7 +188,7 @@ void C4FoWLightSection::Update(C4Rect RectIn)
// Update right end coordinates // Update right end coordinates
#ifdef LIGHT_DEBUG #ifdef LIGHT_DEBUG
LogSilentF("End ray is %s", pEnd->getDesc().getData()); LogSilentF("End beam is %s", pEnd->getDesc().getData());
#endif #endif
if (pEnd->isRight(iRX, iRY)) { if (pEnd->isRight(iRX, iRY)) {
@ -208,29 +208,29 @@ void C4FoWLightSection::Update(C4Rect RectIn)
int32_t y; int32_t y;
for(y = Max(0, iStartY); y < iEndY; y++) { for(y = Max(0, iStartY); y < iEndY; y++) {
// Scan all rays // Scan all beams
C4FoWRay *pLast = pStart; int32_t iDirty = 0; C4FoWBeam *pLast = pStart; int32_t iDirty = 0;
for(C4FoWRay *pRay = pStart ? pStart->getNext() : pRays; pRay; pLast = pRay, pRay = pRay->getNext()) { for(C4FoWBeam *pBeam = pStart ? pStart->getNext() : pBeams; pBeam; pLast = pBeam, pBeam = pBeam->getNext()) {
assert(pLast ? pLast->getNext() == pRay : pRay == pRays); assert(pLast ? pLast->getNext() == pBeam : pBeam == pBeams);
// Clean (enough)? // Clean (enough)?
if (!pRay->isDirty() || y < pRay->getLeftEndY()) if (!pBeam->isDirty() || y < pBeam->getLeftEndY())
continue; continue;
// Out left? // Out left?
if (pRay->isRight(Rect.x, y)) if (pBeam->isRight(Rect.x, y))
continue; continue;
// Out right? // Out right?
if (pRay->isLeft(Rect.x + Rect.Wdt, y) || pRay->isLeft(iRX, iRY)) if (pBeam->isLeft(Rect.x + Rect.Wdt, y) || pBeam->isLeft(iRX, iRY))
break; break;
// We have an active ray that we're about to scan // We have an active beam that we're about to scan
iDirty++; iDirty++;
pRay->Dirty(y+1); pBeam->Dirty(y+1);
// Do a scan // Do a scan
int32_t xl = Max(pRay->getLeftX(y), Bounds.x), int32_t xl = Max(pBeam->getLeftX(y), Bounds.x),
xr = Min(pRay->getRightX(y), Bounds.x+Bounds.Wdt-1); xr = Min(pBeam->getRightX(y), Bounds.x+Bounds.Wdt-1);
for(int x = xl; x <= xr; x++) { for(int x = xl; x <= xr; x++) {
// Fast free? // Fast free?
@ -246,13 +246,13 @@ void C4FoWLightSection::Update(C4Rect RectIn)
// Split points // Split points
int x1 = x - 1, x2 = x + 1; int x1 = x - 1, x2 = x + 1;
bool fSplitLeft = !pRay->isLeft(x1, y); bool fSplitLeft = !pBeam->isLeft(x1, y);
bool fSplitRight = !pRay->isRight(x2, y); bool fSplitRight = !pBeam->isRight(x2, y);
// Double merge? // Double merge?
if (!fSplitLeft && !fSplitRight && pLast && pRay->getNext()) { if (!fSplitLeft && !fSplitRight && pLast && pBeam->getNext()) {
if(pLast->Eliminate(x, y)) { if(pLast->Eliminate(x, y)) {
pRay = pLast; pBeam = pLast;
break; // no typo. fSplitRight => x == xr break; // no typo. fSplitRight => x == xr
} }
} }
@ -260,37 +260,37 @@ void C4FoWLightSection::Update(C4Rect RectIn)
// Merge possible? // Merge possible?
if (!fSplitLeft && fSplitRight && pLast) if (!fSplitLeft && fSplitRight && pLast)
if (pLast->MergeRight(x2, y)) { if (pLast->MergeRight(x2, y)) {
pRay->SetLeft(x2, y); pBeam->SetLeft(x2, y);
assert(pRay->isDirty()); assert(pBeam->isDirty());
continue; continue;
} }
if (fSplitLeft && !fSplitRight && pRay->getNext()) if (fSplitLeft && !fSplitRight && pBeam->getNext())
if (pRay->getNext()->MergeLeft(x1, y)) { if (pBeam->getNext()->MergeLeft(x1, y)) {
pRay->SetRight(x1, y); pBeam->SetRight(x1, y);
break; // no typo. fSplitRight => x == xr break; // no typo. fSplitRight => x == xr
} }
// Split out left // Split out left
if (fSplitLeft) { if (fSplitLeft) {
pLast = pRay; pLast = pBeam;
pRay = pLast->Split(x1, y); pBeam = pLast->Split(x1, y);
assert(pLast->getNext() == pRay); assert(pLast->getNext() == pBeam);
} }
// Split out right // Split out right
if(fSplitRight) { if(fSplitRight) {
pLast = pRay; pLast = pBeam;
pRay = pLast->Split(x2, y); pBeam = pLast->Split(x2, y);
assert(pLast->getNext() == pRay); assert(pLast->getNext() == pBeam);
// Deactivate left/middle ray // Deactivate left/middle beam
pLast->Clean(y); pLast->Clean(y);
assert(pRay->isDirty()); assert(pBeam->isDirty());
} else { } else {
// Deactivate ray // Deactivate beam
pRay->Clean(y); pBeam->Clean(y);
break; break;
} }
@ -299,26 +299,26 @@ void C4FoWLightSection::Update(C4Rect RectIn)
} }
// No active rays left? // No active beams left?
if (!iDirty) if (!iDirty)
break; break;
} }
// At end of light's reach? Mark all rays that got scanned all the way to the end as clean. // At end of light's reach? Mark all beams that got scanned all the way to the end as clean.
// There's no need to scan them anymore. // There's no need to scan them anymore.
if (y >= pLight->getReach()) { if (y >= pLight->getReach()) {
for (C4FoWRay *pRay = pStart ? pStart->getNext() : pRays; pRay; pRay = pRay->getNext()) for (C4FoWBeam *pBeam = pStart ? pStart->getNext() : pBeams; pBeam; pBeam = pBeam->getNext())
if (pRay->isDirty() && pRay->getLeftEndY() > pLight->getReach()) if (pBeam->isDirty() && pBeam->getLeftEndY() > pLight->getReach())
pRay->Clean(pLight->getReach()); pBeam->Clean(pLight->getReach());
} }
#ifdef LIGHT_DEBUG #ifdef LIGHT_DEBUG
LogSilentF("Updated ray list:"); LogSilentF("Updated beam list:");
for(C4FoWRay *pRay = pStart ? pStart->getNext() : pRays; pRay; pRay = pRay->getNext()) { for(C4FoWBeam *pBeam = pStart ? pStart->getNext() : pBeams; pBeam; pBeam = pBeam->getNext()) {
if (pRay->isLeft(iRX, iRY)) if (pBeam->isLeft(iRX, iRY))
break; break;
LogSilent(pRay->getDesc().getData()); LogSilent(pBeam->getDesc().getData());
} }
#endif #endif
} }
@ -336,32 +336,32 @@ void C4FoWLightSection::Invalidate(C4Rect r)
// Assume normalized rectangle // Assume normalized rectangle
assert(r.Wdt > 0 && r.Hgt > 0); assert(r.Wdt > 0 && r.Hgt > 0);
// Get rectangle corners that bound the possibly affected rays // Get rectangle corners that bound the possibly affected beams
int iLY = RectLeftMostY(r), iRY = RectRightMostY(r); int iLY = RectLeftMostY(r), iRY = RectRightMostY(r);
C4FoWRay *pLast = FindRayLeftOf(r.x, iLY); C4FoWBeam *pLast = FindBeamLeftOf(r.x, iLY);
C4FoWRay *pRay = pLast ? pLast->getNext() : pRays; C4FoWBeam *pBeam = pLast ? pLast->getNext() : pBeams;
// Scan over rays // Scan over beams
while (pRay && !pRay->isLeft(r.x+r.Wdt, iRY)) { while (pBeam && !pBeam->isLeft(r.x+r.Wdt, iRY)) {
// Dirty ray? // Dirty beam?
if (pRay->getLeftEndY() > r.y || pRay->getRightEndY() > r.y) if (pBeam->getLeftEndY() > r.y || pBeam->getRightEndY() > r.y)
pRay->Dirty(r.y); pBeam->Dirty(r.y);
// Merge with last ray? // Merge with last beam?
if (pLast && pLast->isDirty() && pRay->isDirty()) { if (pLast && pLast->isDirty() && pBeam->isDirty()) {
pLast->MergeDirty(); pLast->MergeDirty();
pRay = pLast->getNext(); pBeam = pLast->getNext();
// Advance otherwise // Advance otherwise
} else { } else {
pLast = pRay; pLast = pBeam;
pRay = pRay->getNext(); pBeam = pBeam->getNext();
} }
} }
// Final check for merging dirty rays on the right end // Final check for merging dirty beams on the right end
if (pLast && pRay && pLast->isDirty() && pRay->isDirty()) if (pLast && pBeam && pLast->isDirty() && pBeam->isDirty())
pLast->MergeDirty(); pLast->MergeDirty();
} }
@ -370,41 +370,41 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
{ {
C4Rect Reg = rtransRect(pRegion->getRegion()); C4Rect Reg = rtransRect(pRegion->getRegion());
// Find start ray // Find start beam
int iLY = Max(0, RectLeftMostY(Reg)), int iLY = Max(0, RectLeftMostY(Reg)),
iRY = Max(0, RectRightMostY(Reg)), iRY = Max(0, RectRightMostY(Reg)),
iRX = Reg.x + Reg.Wdt; iRX = Reg.x + Reg.Wdt;
C4FoWRay *pStart = FindRayOver(Reg.x, iLY); C4FoWBeam *pStart = FindBeamOver(Reg.x, iLY);
// Find end ray - determine the number of rays we actually need to draw // Find end beam - determine the number of beams we actually need to draw
C4FoWRay *pRay = pStart; int32_t iRayCnt = 0; C4FoWBeam *pBeam = pStart; int32_t iBeamCnt = 0;
while (pRay && !pRay->isLeft(iRX, iRY)) { while (pBeam && !pBeam->isLeft(iRX, iRY)) {
// TODO: Remove clipped rays on the left so we can in the // TODO: Remove clipped beams on the left so we can in the
// most extreme case completely ignore lights here? // most extreme case completely ignore lights here?
pRay = pRay->getNext(); pBeam = pBeam->getNext();
iRayCnt++; iBeamCnt++;
} }
int32_t iOriginalRayCnt = iRayCnt; int32_t iOriginalBeamCnt = iBeamCnt;
// Allocate arrays for our points (lots of them) // Allocate arbeams for our points (lots of them)
float *gFanLX = new float [iRayCnt * 10], float *gFanLX = new float [iBeamCnt * 10],
*gFanLY = gFanLX + iRayCnt, *gFanLY = gFanLX + iBeamCnt,
*gFanRX = gFanLY + iRayCnt, *gFanRX = gFanLY + iBeamCnt,
*gFanRY = gFanRX + iRayCnt, *gFanRY = gFanRX + iBeamCnt,
*gFadeLX = gFanRY + iRayCnt, *gFadeLX = gFanRY + iBeamCnt,
*gFadeLY = gFadeLX + iRayCnt, *gFadeLY = gFadeLX + iBeamCnt,
*gFadeRX = gFadeLY + iRayCnt, *gFadeRX = gFadeLY + iBeamCnt,
*gFadeRY = gFadeRX + iRayCnt, *gFadeRY = gFadeRX + iBeamCnt,
*gFadeIX = gFadeRY + iRayCnt, *gFadeIX = gFadeRY + iBeamCnt,
*gFadeIY = gFadeIX + iRayCnt; *gFadeIY = gFadeIX + iBeamCnt;
int32_t i; int32_t i;
for (i = 0, pRay = pStart; i < iRayCnt; i++, pRay = pRay->getNext()) { for (i = 0, pBeam = pStart; i < iBeamCnt; i++, pBeam = pBeam->getNext()) {
gFanLX[i] = pRay->getLeftEndXf(); gFanLX[i] = pBeam->getLeftEndXf();
gFanLY[i] = float(pRay->getLeftEndY()); gFanLY[i] = float(pBeam->getLeftEndY());
gFanRX[i] = pRay->getRightEndXf(); gFanRX[i] = pBeam->getRightEndXf();
gFanRY[i] = float(pRay->getRightEndY()); gFanRY[i] = float(pBeam->getRightEndY());
} }
// Outputs the rightmost (l=1) or leftmost (l=-1) position of the // Outputs the rightmost (l=1) or leftmost (l=-1) position of the
@ -421,9 +421,9 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
gOutX = s / d * l * y; \ gOutX = s / d * l * y; \
gOutY = s / d * l * -x; \ gOutY = s / d * l * -x; \
} }
// Sadly, this causes rays to cross when the light shrinks... // Sadly, this causes beams to cross when the light shrinks...
#else #else
// I think this is the only version that guarantees no crossing rays. // I think this is the only version that guarantees no crossing beams.
// Unsatisfying as it might be. // Unsatisfying as it might be.
const float gLightShrink = 1.1f; const float gLightShrink = 1.1f;
#define CALC_LIGHT(x, y, l, gOutX, gOutY) \ #define CALC_LIGHT(x, y, l, gOutX, gOutY) \
@ -440,11 +440,11 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
float gScanLevel = 0; float gScanLevel = 0;
for (int iStep = 0; iStep < 100000; iStep++) { for (int iStep = 0; iStep < 100000; iStep++) {
// Find the ray to project. This makes this whole algorithm O(n²), // Find the beam to project. This makes this whole algorithm O(n²),
// but I see no other way to make the whole thing robust :/ // but I see no other way to make the whole thing robust :/
float gBestLevel = FLT_MAX; float gBestLevel = FLT_MAX;
int j; int j;
for (j = 0; j+1 < iRayCnt; j++) { for (j = 0; j+1 < iBeamCnt; j++) {
float gLevel = Min(gFanRY[j], gFanLY[j+1]); float gLevel = Min(gFanRY[j], gFanLY[j+1]);
if (gLevel <= gScanLevel || gLevel >= gBestLevel) if (gLevel <= gScanLevel || gLevel >= gBestLevel)
continue; continue;
@ -454,7 +454,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
break; break;
gScanLevel = gBestLevel; gScanLevel = gBestLevel;
for(int i = 0; i+1 < iRayCnt; i++) { for(int i = 0; i+1 < iBeamCnt; i++) {
if(Min(gFanRY[i], gFanLY[i+1]) != gBestLevel) if(Min(gFanRY[i], gFanLY[i+1]) != gBestLevel)
continue; continue;
@ -463,13 +463,13 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
// #define FAN_STEP_DEBUG // #define FAN_STEP_DEBUG
#ifdef FAN_STEP_DEBUG #ifdef FAN_STEP_DEBUG
LogSilentF("Fan step %d (i=%d)", iStep, i); LogSilentF("Fan step %d (i=%d)", iStep, i);
for (j = 0; j < iRayCnt; j++) { for (j = 0; j < iBeamCnt; j++) {
LogSilentF(" %.02f %.02f", gFanLX[j], gFanLY[j]); LogSilentF(" %.02f %.02f", gFanLX[j], gFanLY[j]);
LogSilentF(" %.02f %.02f", gFanRX[j], gFanRY[j]); LogSilentF(" %.02f %.02f", gFanRX[j], gFanRY[j]);
} }
#endif #endif
// Calculate light bounds. We assume a "smaller" light for closer rays // Calculate light bounds. We assume a "smaller" light for closer beams
CALC_LIGHT(gFanRX[i], gFanRY[i], -1, gLightLX, gLightLY); CALC_LIGHT(gFanRX[i], gFanRY[i], -1, gLightLX, gLightLY);
CALC_LIGHT(gFanLX[i+1], gFanLY[i+1], 1, gLightRX, gLightRY); CALC_LIGHT(gFanLX[i+1], gFanLY[i+1], 1, gLightRX, gLightRY);
@ -479,8 +479,8 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
if (gFanRY[i] > gFanLY[i+1]) { if (gFanRY[i] > gFanLY[i+1]) {
// Left ray surface self-shadowing? We test whether the scalar product // Left beam surface self-shadowing? We test whether the scalar product
// of the ray's normal and the light vector is positive. // of the beam's normal and the light vector is positive.
if ( (gFanRY[i] - gFanLY[i]) * (gFanLX[i+1] - gLightRX) >= if ( (gFanRY[i] - gFanLY[i]) * (gFanLX[i+1] - gLightRX) >=
(gFanRX[i] - gFanLX[i]) * (gFanLY[i+1] - gLightRY)) { (gFanRX[i] - gFanLX[i]) * (gFanLY[i+1] - gLightRY)) {
@ -491,7 +491,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
gFanLY[i] = gFanRY[i]; gFanLY[i] = gFanRY[i];
} }
// Left ray reduced? // Left beam reduced?
float gFanRXp = gFanRX[i]; float thresh = 1.0; float gFanRXp = gFanRX[i]; float thresh = 1.0;
if (gFanRX[i] == gFanLX[i] && gFanRY[i] == gFanLY[i]) { if (gFanRX[i] == gFanLX[i] && gFanRY[i] == gFanLY[i]) {
@ -504,7 +504,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
thresh = 0.0; thresh = 0.0;
} }
// Move right point of left ray to the left (the original point is partly shadowed) // Move right point of left beam to the left (the original point is partly shadowed)
bool fEliminate = false; float b; bool fEliminate = false; float b;
bool f = find_cross(gLightRX, gLightRY, gFanLX[i+1], gFanLY[i+1], bool f = find_cross(gLightRX, gLightRY, gFanLX[i+1], gFanLY[i+1],
gFanLX[i], gFanLY[i], gFanRXp, gFanRY[i], gFanLX[i], gFanLY[i], gFanRXp, gFanRY[i],
@ -522,7 +522,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
// Cross point actually right of surface? This can happen when // Cross point actually right of surface? This can happen when
// we eliminated surfaces. It means that the light doesn't reach // we eliminated surfaces. It means that the light doesn't reach
// down far enough between this and the next ray to hit anything. // down far enough between this and the next beam to hit anything.
// As a result, we insert a new zero-width surface where the light // As a result, we insert a new zero-width surface where the light
// stops. // stops.
} else if (b < 0.0) { } else if (b < 0.0) {
@ -548,7 +548,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
if (fEliminate && i) { if (fEliminate && i) {
// Remove it then // Remove it then
for(int j = i; j+1 < iRayCnt; j++) { for(int j = i; j+1 < iBeamCnt; j++) {
gFanLX[j] = gFanLX[j+1]; gFanLX[j] = gFanLX[j+1];
gFanLY[j] = gFanLY[j+1]; gFanLY[j] = gFanLY[j+1];
gFanRX[j] = gFanRX[j+1]; gFanRX[j] = gFanRX[j+1];
@ -556,10 +556,10 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
} }
// With the elimination, we need to re-process the last // With the elimination, we need to re-process the last
// ray, as it might be more shadowed than we realized. // beam, as it might be more shadowed than we realized.
// Note that the last point might have been projected already - // Note that the last point might have been projected already -
// but that's okay, // but that's okay,
iRayCnt--; i-=2; iBeamCnt--; i-=2;
} }
// Descending - same, but mirrored. And without comments. // Descending - same, but mirrored. And without comments.
@ -590,14 +590,14 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
gFanLY[i+1] = gCrossY; gFanLY[i+1] = gCrossY;
} }
assert(gFanRY[i] < gFanLY[i+1]); assert(gFanRY[i] < gFanLY[i+1]);
if (fEliminate && i+2 < iRayCnt) { if (fEliminate && i+2 < iBeamCnt) {
for(int j = i+1; j+1 < iRayCnt; j++) { for(int j = i+1; j+1 < iBeamCnt; j++) {
gFanLX[j] = gFanLX[j+1]; gFanLX[j] = gFanLX[j+1];
gFanLY[j] = gFanLY[j+1]; gFanLY[j] = gFanLY[j+1];
gFanRX[j] = gFanRX[j+1]; gFanRX[j] = gFanRX[j+1];
gFanRY[j] = gFanRY[j+1]; gFanRY[j] = gFanRY[j+1];
} }
iRayCnt--; i--; iBeamCnt--; i--;
} }
} }
@ -620,8 +620,8 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
continue; continue;
// This should always follow an elimination, but better check // This should always follow an elimination, but better check
assert(iOriginalRayCnt > iRayCnt); assert(iOriginalBeamCnt > iBeamCnt);
for (int j = iRayCnt - 1; j >= i+1; j--) { for (int j = iBeamCnt - 1; j >= i+1; j--) {
gFanLX[j+1] = gFanLX[j]; gFanLX[j+1] = gFanLX[j];
gFanLY[j+1] = gFanLY[j]; gFanLY[j+1] = gFanLY[j];
gFanRX[j+1] = gFanRX[j]; gFanRX[j+1] = gFanRX[j];
@ -634,12 +634,12 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
gFanRX[i+1] = gCrossX; gFanRX[i+1] = gCrossX;
gFanRY[i+1] = gCrossY; gFanRY[i+1] = gCrossY;
// Jump over surface. Note that our right ray might get // Jump over surface. Note that our right beam might get
// eliminated later on, causing us to back-track into this // eliminated later on, causing us to back-track into this
// zero-length pseudo-surface. This will cause find_cross // zero-length pseudo-surface. This will cause find_cross
// above to eliminate the pseudo-surface and back-track // above to eliminate the pseudo-surface and back-track
// further to the left, which is exactly how it should work. // further to the left, which is exactly how it should work.
iRayCnt++; i++; iBeamCnt++; i++;
} }
} }
@ -649,7 +649,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
#ifdef FAN_STEP_DEBUG #ifdef FAN_STEP_DEBUG
LogSilent("Fade points"); LogSilent("Fade points");
#endif // FAN_STEP_DEBUG #endif // FAN_STEP_DEBUG
for (i = 0; i < iRayCnt; i++) { for (i = 0; i < iBeamCnt; i++) {
// Calculate light bounds. Note that the way light size is calculated // Calculate light bounds. Note that the way light size is calculated
// and we are using it below, we need to consider an "asymetrical" light. // and we are using it below, we need to consider an "asymetrical" light.
@ -695,12 +695,12 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
#define NEWER_INTER_FADE_CODE #define NEWER_INTER_FADE_CODE
//#define NEW_INTER_FADE_CODE //#define NEW_INTER_FADE_CODE
#ifdef NEWER_INTER_FADE_CODE #ifdef NEWER_INTER_FADE_CODE
pRay = pStart; pBeam = pStart;
#endif #endif
bool *fAscend = new bool[iRayCnt]; bool *fAscend = new bool[iBeamCnt];
for (i = 0; i+1 < iRayCnt; i++) { for (i = 0; i+1 < iBeamCnt; i++) {
// Calculate light bounds. We assume a "smaller" light for closer rays // Calculate light bounds. We assume a "smaller" light for closer beams
CALC_LIGHT(gFanRX[i], gFanRY[i], 1, gLightLX, gLightLY); CALC_LIGHT(gFanRX[i], gFanRY[i], 1, gLightLX, gLightLY);
CALC_LIGHT(gFanLX[i+1], gFanLY[i+1], -1, gLightRX, gLightRY); CALC_LIGHT(gFanLX[i+1], gFanLY[i+1], -1, gLightRX, gLightRY);
@ -708,8 +708,8 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
// Midpoint // Midpoint
float mx = (gFadeRX[i] + gFadeLX[i+1]) / 2, float mx = (gFadeRX[i] + gFadeLX[i+1]) / 2,
my = (gFadeRY[i] + gFadeLY[i+1]) / 2; my = (gFadeRY[i] + gFadeLY[i+1]) / 2;
while (pRay->getNext() && pRay->isRight(mx, my)) while (pBeam->getNext() && pBeam->isRight(mx, my))
pRay = pRay->getNext(); pBeam = pBeam->getNext();
#endif #endif
// Ascending? // Ascending?
@ -720,7 +720,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
float dx, dy; float dx, dy;
find_cross(0,0, mx, my, find_cross(0,0, mx, my,
pRay->getLeftEndXf(), pRay->getLeftEndY(), pRay->getRightEndXf(), pRay->getRightEndY(), pBeam->getLeftEndXf(), pBeam->getLeftEndY(), pBeam->getRightEndXf(), pBeam->getRightEndY(),
&dx, &dy); &dx, &dy);
float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy); float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy);
gFadeIX[i] = mx + d * dx; gFadeIX[i] = mx + d * dx;
@ -744,7 +744,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
gFadeIX[i] = gFadeLX[i+1]; gFadeIX[i] = gFadeLX[i+1];
gFadeIY[i] = gFadeLY[i+1]; gFadeIY[i] = gFadeLY[i+1];
// Project on left ray's height where necessary // Project on left beam's height where necessary
if (gFadeIY[i] < gFanRY[i]) { if (gFadeIY[i] < gFanRY[i]) {
float d = (gFanRY[i] - gFadeIY[i]) / (gFadeIY[i] - gLightY); float d = (gFanRY[i] - gFadeIY[i]) / (gFadeIY[i] - gLightY);
gFadeIX[i] += d * (gFadeIX[i] - gLightRX); gFadeIX[i] += d * (gFadeIX[i] - gLightRX);
@ -759,7 +759,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
float dx, dy; float dx, dy;
find_cross(0,0, mx,my, find_cross(0,0, mx,my,
pRay->getLeftEndXf(), pRay->getLeftEndY(), pRay->getRightEndXf(), pRay->getRightEndY(), pBeam->getLeftEndXf(), pBeam->getLeftEndY(), pBeam->getRightEndXf(), pBeam->getRightEndY(),
&dx, &dy); &dx, &dy);
float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy) / 2; float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy) / 2;
gFadeIX[i] = mx + d * dx; gFadeIX[i] = mx + d * dx;
@ -783,7 +783,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
gFadeIX[i] = gFadeRX[i]; gFadeIX[i] = gFadeRX[i];
gFadeIY[i] = gFadeRY[i]; gFadeIY[i] = gFadeRY[i];
// Project on right ray's height where necessary // Project on right beam's height where necessary
if (gFadeIY[i] < gFanLY[i+1]) { if (gFadeIY[i] < gFanLY[i+1]) {
float d = (gFanLY[i+1] - gFadeIY[i]) / (gFadeIY[i] - gLightY); float d = (gFanLY[i+1] - gFadeIY[i]) / (gFadeIY[i] - gLightY);
gFadeIX[i] += d * (gFadeIX[i] - gLightLX); gFadeIX[i] += d * (gFadeIX[i] - gLightLX);
@ -800,9 +800,9 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
// Phase 4: Transform all points into region coordinates // Phase 4: Transform all points into region coordinates
for (i = 0; i < 5; i++) { for (i = 0; i < 5; i++) {
float *pX = gFanLX + 2 * i * iOriginalRayCnt, float *pX = gFanLX + 2 * i * iOriginalBeamCnt,
*pY = gFanLY + 2 * i * iOriginalRayCnt; *pY = gFanLY + 2 * i * iOriginalBeamCnt;
for (int32_t j = 0; j < iRayCnt; j++) { for (int32_t j = 0; j < iBeamCnt; j++) {
float x = pX[j], y = pY[j]; float x = pX[j], y = pY[j];
if (pOnScreen) if (pOnScreen)
{ {
@ -879,7 +879,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
if (!pOnScreen) { if (!pOnScreen) {
LIGHT(gLightX, gLightY); LIGHT(gLightX, gLightY);
} }
for (i = 0; i < iRayCnt; i++) { for (i = 0; i < iBeamCnt; i++) {
if (i == 0 || gFanRX[i-1] != gFanLX[i] || gFanRY[i-1] != gFanLY[i]) { if (i == 0 || gFanRX[i-1] != gFanLX[i] || gFanRY[i-1] != gFanLY[i]) {
LIGHT(gFanLX[i], gFanLY[i]); LIGHT(gFanLX[i], gFanLY[i]);
} }
@ -893,7 +893,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
glShadeModel(GL_SMOOTH); glShadeModel(GL_SMOOTH);
if(!pOnScreen) glBegin(GL_TRIANGLES); if(!pOnScreen) glBegin(GL_TRIANGLES);
for (i = 0; i < iRayCnt; i++) { for (i = 0; i < iBeamCnt; i++) {
// The quad. Will be empty if fan points match // The quad. Will be empty if fan points match
if (gFanLX[i] != gFanRX[i] || gFanLY[i] != gFanRY[i]) { if (gFanLX[i] != gFanRX[i] || gFanLY[i] != gFanRY[i]) {
@ -916,7 +916,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
} }
// No intermediate fade for last point // No intermediate fade for last point
if (i+1 >= iRayCnt) continue; if (i+1 >= iBeamCnt) continue;
// Ascending? // Ascending?
if (fAscend[i]) { if (fAscend[i]) {

View File

@ -5,9 +5,9 @@
#include "C4FoWLight.h" #include "C4FoWLight.h"
class C4FoWRegion; class C4FoWRegion;
class C4FoWRay; class C4FoWBeam;
/** The light section manages the rays for one light for one direction of 90°. */ /** The light section manages the beams for one light for one direction of 90°. */
class C4FoWLightSection class C4FoWLightSection
{ {
public: public:
@ -24,8 +24,8 @@ private:
int a, b, c, d; int a, b, c, d;
int ra, rb, rc, rd; int ra, rb, rc, rd;
/* This section's rays */ /* This section's beams */
class C4FoWRay *pRays; class C4FoWBeam *pBeams;
C4FoWLightSection *pNext; C4FoWLightSection *pNext;
@ -33,38 +33,38 @@ public:
C4FoWLightSection *getNext() const { return pNext; } C4FoWLightSection *getNext() const { return pNext; }
/** Recalculate of all light rays within the given rectangle because the landscape changed. */ /** Recalculate of all light beams within the given rectangle because the landscape changed. */
void Invalidate(C4Rect r); void Invalidate(C4Rect r);
/** Update all light rays within the given rectangle */ /** Update all light beams within the given rectangle */
void Update(C4Rect r); void Update(C4Rect r);
void Render(C4FoWRegion *pRegion, const class C4TargetFacet *pOnScreen = NULL); void Render(C4FoWRegion *pRegion, const class C4TargetFacet *pOnScreen = NULL);
/** Shorten all light rays to the given reach. /** Shorten all light beams to the given reach.
Called when the size of the light has decreased to the given value */ Called when the size of the light has decreased to the given value */
void Prune(int32_t iReach); void Prune(int32_t iReach);
/** Extend all light rays to the given reach. /** Extend all light beams to the given reach.
Called when the size of the light has increased to the given value */ Called when the size of the light has increased to the given value */
void Dirty(int32_t iReach); void Dirty(int32_t iReach);
private: private:
/** Remove all rays. pRays is NULL after that. */ /** Remove all beams. pBeams is NULL after that. */
void ClearRays(); void ClearBeams();
// Ray coordinate to landscape coordinate. Ray coordinates are relative to the light source. // Beam coordinate to landscape coordinate. Beam coordinates are relative to the light source.
template <class T> T transDX(T dx, T dy) const { return T(a) * dx + T(b) * dy; } template <class T> T transDX(T dx, T dy) const { return T(a) * dx + T(b) * dy; }
template <class T> T transDY(T dx, T dy) const { return T(c) * dx + T(d) * dy; } template <class T> T transDY(T dx, T dy) const { return T(c) * dx + T(d) * dy; }
template <class T> T transX(T x, T y) const { return transDX(x, y) + T(pLight->getX()); } template <class T> T transX(T x, T y) const { return transDX(x, y) + T(pLight->getX()); }
template <class T> T transY(T x, T y) const { return transDY(x, y) + T(pLight->getY()); } template <class T> T transY(T x, T y) const { return transDY(x, y) + T(pLight->getY()); }
// Landscape coordinate to ray coordinate. Ray coordinates are relative to the light source. // Landscape coordinate to beam coordinate. Beam coordinates are relative to the light source.
template <class T> T rtransDX(T dx, T dy) const { return T(ra) * dx + T(rb) * dy; } template <class T> T rtransDX(T dx, T dy) const { return T(ra) * dx + T(rb) * dy; }
template <class T> T rtransDY(T dx, T dy) const { return T(rc) * dx + T(rd) * dy; } template <class T> T rtransDY(T dx, T dy) const { return T(rc) * dx + T(rd) * dy; }
template <class T> T rtransX(T x, T y) const { return rtransDX(x-T(pLight->getX()),y-T(pLight->getY())); } template <class T> T rtransX(T x, T y) const { return rtransDX(x-T(pLight->getX()),y-T(pLight->getY())); }
template <class T> T rtransY(T x, T y) const { return rtransDY(x-T(pLight->getX()),y-T(pLight->getY())); } template <class T> T rtransY(T x, T y) const { return rtransDY(x-T(pLight->getX()),y-T(pLight->getY())); }
/** Returns a rectangle in ray coordinates */ /** Returns a rectangle in beam coordinates */
C4Rect rtransRect(C4Rect r) const { C4Rect rtransRect(C4Rect r) const {
C4Rect Rect(rtransX(r.x, r.y), rtransY(r.x, r.y), C4Rect Rect(rtransX(r.x, r.y), rtransY(r.x, r.y),
rtransDX(r.Wdt, r.Hgt), rtransDY(r.Wdt, r.Hgt)); rtransDX(r.Wdt, r.Hgt), rtransDY(r.Wdt, r.Hgt));
@ -81,10 +81,10 @@ private:
This function assumes a cartesian coordinate system (y axis up) */ This function assumes a cartesian coordinate system (y axis up) */
int32_t RectRightMostY(const C4Rect &r) const { return r.x + r.Wdt <= 0 ? r.y+r.Hgt : r.y; } int32_t RectRightMostY(const C4Rect &r) const { return r.x + r.Wdt <= 0 ? r.y+r.Hgt : r.y; }
/** Find right-most ray left of point */ /** Find right-most beam left of point */
C4FoWRay *FindRayLeftOf(int32_t x, int32_t y); C4FoWBeam *FindBeamLeftOf(int32_t x, int32_t y);
/** Find left-most ray to extend over point */ /** Find left-most beam to extend over point */
C4FoWRay *FindRayOver(int32_t x, int32_t y); C4FoWBeam *FindBeamOver(int32_t x, int32_t y);
}; };

View File

@ -1,98 +0,0 @@
#ifndef C4FOWRAY_H
#define C4FOWRAY_H
#include "StdBuf.h"
/** This class represents one ray. ...TODO*/
class C4FoWRay
{
public:
C4FoWRay(int32_t iLeftX, int32_t iLeftY, int32_t iRightX, int32_t iRightY)
: iLeftX(iLeftX), iLeftY(iLeftY), iRightX(iRightX), iRightY(iRightY),
iLeftEndY(0), iRightEndY(0),
iError(0),
fDirty(true),
pNext(NULL)
{ }
private:
int32_t iLeftX, iLeftY; // left delimiter point
int32_t iRightX, iRightY; // right delimiter point
int32_t iLeftEndY, iRightEndY; // where it hit solid material. C4FoWRayActive while currently being followed.
int32_t iError; // How much error this ray has
bool fDirty; // landscape changed since it was followed?
C4FoWRay *pNext;
public:
bool isDirty() const { return fDirty; }
bool isClean() const { return !fDirty; }
C4FoWRay *getNext() const { return pNext; }
// Get a point on the ray boundary.
inline int32_t getLeftX(int32_t y) const { return iLeftX * y / iLeftY; }
inline int32_t getRightX(int32_t y) const { return iRightX * y / iRightY; }
inline float getLeftXf(int32_t y) const { return (iLeftX * y) / float(iLeftY); }
inline float getRightXf(int32_t y) const { return (iRightX * y) / float(iRightY); }
// TODO: why is iLeftY or iRightY never 0?
int32_t getLeftEndY() const { return iLeftEndY; }
int32_t getLeftEndX() const { return getLeftX(iLeftEndY); }
float getLeftEndXf() const { return getLeftXf(iLeftEndY); }
int32_t getRightEndY() const { return iRightEndY; }
int32_t getRightEndX() const { return getRightX(iRightEndY); }
float getRightEndXf() const { return getRightXf(iRightEndY); }
StdStrBuf getDesc() const;
/* returns whether the given point is left of an imaginery line drawn from the left delimiter point to the origin */
bool isLeft(int x, int y) const {
return iLeftX * y > x * iLeftY;
}
/* returns whether the given point is right of an imaginery line drawn from the right delimiter point to the origin */
bool isRight(int x, int y) const {
return iRightX * y < x * iRightY;
}
/* returns whether the given point is inside a cone spanned by the ray between the left delimiter and right
delimiter point and the ray spanned from the origin to the left delimiter point.
In case the origin is on the line with the two delimiter points, this function returns whether the point
is on the line spanned by the left and right delimiter points. */
bool isInside(int x, int y) const {
return !isLeft(x, y) && !isRight(x, y);
}
void SetLeft(int x, int y) { iLeftX = x; iLeftY = y; }
void SetRight(int x, int y) { iRightX = x; iRightY = y; }
bool MergeRight(int x, int y);
bool MergeLeft(int x, int y);
/* Split this ray into two: this ray and the returned one. The given point x,y is the position at
which the two resulting rays are connected with their left/right endpoints.
It is asserted that the given point is on the same line as the delimiting points. */
C4FoWRay *Split(int x, int y);
/* Remove the two following vertices and connect this ray to the right delimiter point of the next but one ray.
The position x,y is only used for the error calcualation and not actually inserted.
Returns false and does not do the action in case the error threshold would be reached with these parameters
*/
// ASK: iError is not added to the error counter of this ray on success
// ASK: this removes TWO rays (but the comment says otherwise), why not one?
// ASK: the x,y coordinate given is never actually added - even thout it counts towards the error counter
bool Eliminate(int x, int y);
void MergeDirty();
void Clean(int32_t y);
void Dirty(int32_t y);
void Prune(int32_t y);
// ASK: why are they called left/right delimiter and not start and endpoints?
// ASK: left delimiter, right delimiter, origin always on one line? (if yes: add assertions)
// TODO: find out more about this DIRTY stuff
};
#endif // C4FOWRAY_H