41 a = 1; b = 0; c = 0; d = 1;
42 ra = 1; rb = 0; rc = 0; rd = 1;
45 a = 0; b = 1; c = -1; d = 0;
46 ra = 0; rb = -1; rc = 1; rd = 0;
49 a = -1; b = 0; c = 0; d = -1;
50 ra = -1; rb = 0; rc = 0; rd = -1;
53 a = 0; b = -1; c =1; d = 0;
54 ra = 0; rb = 1; rc = -1; rd = 0;
66 inline void C4FoWLightSection::LightBallExtremePoint(
float x,
float y,
float dir,
float &lightX,
float &lightY)
const
68 float d = sqrt(x * x + y * y);
69 float s = std::min(
float(pLight->
getSize()), d / 5.0f);
70 lightX = dir * y *
s / d;
71 lightY = dir * -x *
s / d;
74 inline void C4FoWLightSection::LightBallRightMostPoint(
float x,
float y,
float &lightX,
float &lightY)
const
75 { LightBallExtremePoint(x,y,+1.0f,lightX,lightY); }
77 inline void C4FoWLightSection::LightBallLeftMostPoint(
float x,
float y,
float &lightX,
float &lightY)
const
78 { LightBallExtremePoint(x,y,-1.0f,lightX,lightY); }
80 template <
class T> T C4FoWLightSection::transDX(T dx, T dy)
const {
return T(a) * dx + T(b) * dy; }
81 template <
class T> T C4FoWLightSection::transDY(T dx, T dy)
const {
return T(c) * dx + T(d) * dy; }
82 template <
class T> T C4FoWLightSection::transX(T x, T y)
const {
return transDX(x, y) + T(pLight->
getX()); }
83 template <
class T> T C4FoWLightSection::transY(T x, T y)
const {
return transDY(x, y) + T(pLight->
getY()); }
85 template <
class T> T C4FoWLightSection::rtransDX(T dx, T dy)
const {
return T(ra) * dx + T(rb) * dy; }
86 template <
class T> T C4FoWLightSection::rtransDY(T dx, T dy)
const {
return T(rc) * dx + T(rd) * dy; }
87 template <
class T> T C4FoWLightSection::rtransX(T x, T y)
const {
return rtransDX(x-T(pLight->
getX()),y-T(pLight->
getY())); }
88 template <
class T> T C4FoWLightSection::rtransY(T x, T y)
const {
return rtransDY(x-T(pLight->
getX()),y-T(pLight->
getY())); }
90 void C4FoWLightSection::ClearBeams()
115 if (beam->getLeftEndY() >= reach || beam->getRightEndY() >= reach)
116 beam->Dirty(std::min(beam->getLeftEndY(), beam->getRightEndY()));
119 C4FoWBeam *C4FoWLightSection::FindBeamLeftOf(int32_t x, int32_t y)
const
123 if (!pBeams || !pBeams->
isRight(x, y))
137 C4Rect Rect = rtransRect(RectIn);
145 beamsString.ApendBeamChar(
' ');
146 beamsString.ApendBeam(beam->
getDesc());
161 if (Rect.
y + Rect.
Hgt < 0)
165 int32_t ly = RectLeftMostY(Rect),
166 lx = RectLeftMostX(Rect),
167 ry = RectRightMostY(Rect),
168 rx = RectRightMostX(Rect);
170 C4FoWBeam *startBeam = FindBeamLeftOf(lx, ly);
174 if (nextBeam->isDirty())
break;
175 startBeam = nextBeam;
185 while (beam && !beam->
isLeft(rx, ry)) {
202 if (endBeam->
isRight(rx, ry)) {
212 for(y = std::max(0, startY); y < endY; y++) {
217 if (y < pLight->getSize()) {
218 ignoreX = int(sqrt(pLight->
getSize() * pLight->
getSize() - y * y));
224 for(
C4FoWBeam *beam = startBeam ? startBeam->
getNext() : pBeams; beam; lastBeam = beam, beam = beam->
getNext())
226 assert(lastBeam ? lastBeam->
getNext() == beam : beam == pBeams);
229 if (!beam->
isDirty() || y < beam->getLeftEndY())
244 int32_t xl = std::max(beam->
getLeftX(y), Bounds.
x),
246 for(int32_t x = xl; x <= xr; x++)
249 if (abs(x) < ignoreX)
261 if (!
GBackSolid(transX(x,y), transY(x,y)))
continue;
264 int32_t x1 = x - 1, x2 = x + 1;
265 bool splitLeft = !beam->
isLeft(x1, y);
266 bool splitRight = !beam->
isRight(x2, y);
269 if (!splitLeft && !splitRight && lastBeam && beam->
getNext())
279 if (!splitLeft && splitRight && lastBeam)
286 if (splitLeft && !splitRight && beam->
getNext())
297 beam = lastBeam->
Split(x1, y);
298 assert(lastBeam->
getNext() == beam);
305 beam = lastBeam->
Split(x2, y);
306 assert(lastBeam->
getNext() == beam);
337 if (beam->
isLeft(iRX, iRY))
346 bool C4FoWLightSection::isConsistent()
const {
347 return (a * c + b * d == 1) && (ra * rc + rb * rd == 1) &&
348 (a * ra + b * rc == 1) && (a * rb + b * rd == 0) &&
349 (c * ra + d * rc == 0) && (c * rb + d * rd == 1);
355 assert(r.
Wdt > 0 && r.
Hgt > 0);
358 int32_t ly = RectLeftMostY(r),
359 lx = RectLeftMostX(r),
360 ry = RectRightMostY(r),
361 rx = RectRightMostX(r);
362 C4FoWBeam *lastBeam = FindBeamLeftOf(lx, ly);
366 while (beam && !beam->
isLeft(rx, ry))
393 if(rect.
y + rect.
Hgt < 0)
return 0;
395 int32_t ly = RectLeftMostY(rect),
396 lx = RectLeftMostX(rect),
397 ry = RectRightMostY(rect),
398 rx = RectRightMostX(rect);
400 C4FoWBeam *pPrev = FindBeamLeftOf(lx, ly);
401 firstBeam = pPrev ? pPrev->
getNext() : pBeams;
405 int32_t beamCount = 0;
406 while (beam && !beam->
isLeft(rx, ry))
420 static inline bool find_cross(
float x1,
float y1,
float x2,
float y2,
421 float x3,
float y3,
float x4,
float y4,
422 float *px,
float *py,
float *pb =
nullptr)
429 float d = (x3-x4)*(y1-y2) - (y3-y4)*(x1-x2);
430 if (d == 0)
return false;
435 float b = ((y4-y2)*(x1-x2) - (x4-x2)*(y1-y2)) / d;
436 *px =
b*x3 + (1-
b)*x4;
437 *py =
b*y3 + (1-
b)*y4;
444 C4FoWBeam *startBeam =
nullptr, *endBeam =
nullptr;
445 int32_t beamCount = FindBeamsClipped(rtransRect(region->
getRegion()), startBeam, endBeam);
446 std::list<C4FoWBeamTriangle> result;
447 float crossX=0.0f, crossY=0.0f;
450 if(!beamCount)
return result;
452 bool isStartClipped = startBeam != pBeams;
453 bool isEndClipped = !!endBeam;
456 for (int32_t i = 0; i < beamCount; i++, beam = beam->
getNext())
463 if(i == 0 && isStartClipped)
465 if(i == beamCount - 1 && isEndClipped)
471 result.push_back(tri);
474 if(result.empty())
return result;
479 for (
int step = 0; step < 100000; step++)
486 float bestLevel = FLT_MAX;
487 for (std::list<C4FoWBeamTriangle>::iterator it = result.begin(), nextIt; it != --result.end(); ++it)
489 nextIt = it; ++nextIt;
490 float level = std::min(it->fanRY, nextIt->fanLY);
491 if (level <= scanLevel || level >= bestLevel)
495 if(bestLevel == FLT_MAX)
497 scanLevel = bestLevel;
503 for(std::list<C4FoWBeamTriangle>::iterator it = result.begin(), nextIt; it != --result.end(); it = nextIt)
505 nextIt = it; ++nextIt;
509 float level = std::min(tri.
fanRY, nextTri.fanLY);
510 if(level != bestLevel)
515 #ifdef FAN_STEP_DEBUG
516 LogSilentF(
"Fan step %d (i=%d)", step, std::distance(result.begin(),it));
517 for (std::list<C4FoWBeamTriangle>::iterator it2 = result.begin(); it2 != result.end(); it2++) {
518 const char *marker =
"";
519 if (it2 == it) marker =
" (it)";
520 if (it2 == nextIt) marker =
" (nextIt)";
521 LogSilentF(
" %.010f %.010f%s", it2->fanLX, it2->fanLY, marker);
522 LogSilentF(
" %.010f %.010f%s", it2->fanRX, it2->fanRY, marker);
527 float lightLX, lightLY, lightRX, lightRY;
528 LightBallLeftMostPoint(tri.
fanRX, tri.
fanRY, lightLX, lightLY);
529 LightBallRightMostPoint(nextTri.fanLX, nextTri.fanLY, lightRX, lightRY);
532 bool descendCollision =
false;
533 const float ascendDescendDelta = 0.1f;
534 if (tri.
fanRY > nextTri.fanLY)
538 if ( (tri.
fanRY - tri.
fanLY) * (nextTri.fanLX - lightRX) >=
539 (tri.
fanRX - tri.
fanLX) * (nextTri.fanLY - lightRY))
553 float threshold = 1.0f;
555 threshold -= ascendDescendDelta / fanWidth;
558 float fanRXp = tri.
fanRX;
572 bool f = find_cross(lightRX, lightRY, nextTri.fanLX, nextTri.fanLY,
574 &crossX, &crossY, &
b);
576 #ifdef FAN_STEP_DEBUG
577 LogSilentF(
"Ascend, b=%.010f, cross=%.010f/%.010f",
b, crossX, crossY);
588 if (it == result.begin())
592 it = nextIt = result.erase(it);
597 if (tri.
fanRY > nextTri.fanLY)
610 descendCollision =
true;
611 LightBallLeftMostPoint(tri.
fanLX, tri.
fanLY, lightLX, lightLY);
625 descendCollision =
true;
633 assert(tri.
fanRY > nextTri.fanLY);
641 else if (tri.
fanRY < nextTri.fanLY)
643 if ( (nextTri.fanRY - nextTri.fanLY) * (tri.
fanRX - lightLX) >=
644 (nextTri.fanRX - nextTri.fanLX) * (tri.
fanRY - lightLY))
646 assert(nextTri.fanLY <= nextTri.fanRY);
647 nextTri.fanRX = nextTri.fanLX;
648 nextTri.fanRY = nextTri.fanLY;
651 float fanRXp = nextTri.fanRX;
652 float threshold = 0.0f;
653 float fanWidth = fabs(nextTri.fanRX - nextTri.fanLX) + fabs(nextTri.fanRY - nextTri.fanLY);
654 threshold += ascendDescendDelta / fanWidth;
655 if (nextTri.fanRX == nextTri.fanLX && nextTri.fanRY == nextTri.fanLY)
659 bool f = find_cross(lightLX, lightLY, tri.
fanRX, tri.
fanRY,
660 nextTri.fanLX, nextTri.fanLY, fanRXp, nextTri.fanRY,
661 &crossX, &crossY, &
b);
662 #ifdef FAN_STEP_DEBUG
663 LogSilentF(
"Descend, b=%.010f, cross=%.010f/%.010f",
b, crossX, crossY);
668 if (nextIt == --result.end())
670 nextIt = result.erase(nextIt);
672 if (nextTri.fanLY > tri.
fanRY)
677 descendCollision =
true;
678 LightBallRightMostPoint(nextTri.fanLX, nextTri.fanLY, lightRX, lightRY);
682 descendCollision =
true;
686 nextTri.fanLX = crossX;
687 nextTri.fanLY = crossY;
688 assert(tri.
fanRY < nextTri.fanLY);
696 if (nextTri.fanLX - tri.
fanRX > 0.5) {
697 descendCollision =
true;
706 assert(descendCollision); (void) descendCollision;
710 bool f = find_cross(lightLX, lightLY, tri.
fanRX, tri.
fanRY,
711 lightRX, lightRY, nextTri.fanLX, nextTri.fanLY,
714 #ifdef FAN_STEP_DEBUG
715 LogSilentF(
"Collision, cross=%.02f/%.02f", crossX, crossY);
721 const float descendEta = 3;
722 if (crossY <= tri.
fanRY + descendEta || crossY <= nextTri.fanLY + descendEta)
726 assert(beamCount >
static_cast<int>(result.size()));
729 newTriangle.
fanLX = crossX;
730 newTriangle.
fanLY = crossY;
731 newTriangle.
fanRX = crossX;
732 newTriangle.
fanRY = crossY;
734 nextIt = result.insert(nextIt, newTriangle);
746 #ifdef FAN_STEP_DEBUG
748 for (std::list<C4FoWBeamTriangle>::iterator it2 = result.begin(); it2 != result.end(); it2++) {
749 LogSilentF(
" %.010f %.010f", it2->fanLX, it2->fanLY);
750 LogSilentF(
" %.010f %.010f", it2->fanRX, it2->fanRY);
755 for (
auto & tri : result)
759 float lightLX, lightLY, lightRX, lightRY;
760 LightBallLeftMostPoint(tri.fanLX, tri.fanLY, lightLX, lightLY);
761 LightBallRightMostPoint(tri.fanRX, tri.fanRY, lightRX, lightRY);
768 float dx = tri.fanLX - lightLX;
769 float dy = tri.fanLY - lightLY;
770 float d = float(pLight->
getFadeout()) / sqrt(dx*dx + dy*dy);
771 tri.fadeLX = tri.fanLX + d * dx;
772 tri.fadeLY = tri.fanLY + d * dy;
774 dx = tri.fanRX - lightRX;
775 dy = tri.fanRY - lightRY;
776 d = float(pLight->
getFadeout()) / sqrt(dx*dx + dy*dy);
777 tri.fadeRX = tri.fanRX + d * dx;
778 tri.fadeRY = tri.fanRY + d * dy;
781 const double fadeCrossEta = 0.01;
782 if ((tri.fadeRX - lightRX) / (tri.fadeRY - lightRY)
783 < (tri.fadeLX - lightRX) / (tri.fadeLY - lightRY) + fadeCrossEta)
786 tri.fadeLX = tri.fadeRX = (tri.fadeLX + tri.fadeRX) / 2;
787 tri.fadeLY = tri.fadeRY = (tri.fadeLY + tri.fadeRY) / 2;
791 transTriangles(result);
796 void C4FoWLightSection::transTriangles(std::list<C4FoWBeamTriangle> &triangles)
const
798 for (
auto & tri : triangles)
802 x = tri.fanRX, y = tri.fanRY;
803 tri.fanRX = transX(x,y);
804 tri.fanRY = transY(x,y);
806 x = tri.fanLX, y = tri.fanLY;
807 tri.fanLX = transX(x,y);
808 tri.fanLY = transY(x,y);
810 x = tri.fadeRX, y = tri.fadeRY;
811 tri.fadeRX = transX(x,y);
812 tri.fadeRY = transY(x,y);
814 x = tri.fadeLX, y = tri.fadeLY;
815 tri.fadeLX = transX(x,y);
816 tri.fadeLY = transY(x,y);
839 int32_t beam_count = 0;
840 pComp->
Value(mkNamingCountAdapt<int32_t>(beam_count,
"Beam"));
842 for (int32_t i = 0; i < beam_count; ++i)
844 std::unique_ptr<C4FoWBeam> beam(
new C4FoWBeam(0, 0, 0, 0));
851 last_beam = new_beam;
bool GBackSolid(int32_t x, int32_t y)
bool LogSilent(const char *szMessage, bool fConsole)
bool LogSilentF(const char *strMessage,...)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
int32_t getRightX(int32_t y) const
bool EliminateRight(int32_t x, int32_t y)
int32_t getRightEndY() const
C4FoWBeam * getNext() const
bool isRight(int32_t x, int32_t y) const
int32_t getLeftEndY() const
void setNext(C4FoWBeam *next)
StdStrBuf getDesc() const
bool MergeRight(int32_t x, int32_t y)
float getLeftEndXf() const
int32_t getLeftX(int32_t y) const
bool MergeLeft(int32_t x, int32_t y)
C4FoWBeam * Split(int32_t x, int32_t y)
void SetLeft(int32_t x, int32_t y)
float getRightEndXf() const
void SetRight(int32_t x, int32_t y)
int32_t getRightEndX() const
bool isLeft(int32_t x, int32_t y) const
int32_t getTotalReach() const
int32_t getFadeout() const
void CompileFunc(StdCompiler *pComp)
C4FoWLightSection(C4FoWLight *pLight, int r)
void Prune(int32_t reach)
void Dirty(int32_t reach)
std::list< C4FoWBeamTriangle > CalculateTriangles(C4FoWRegion *region) const
void Invalidate(C4Rect r)
const C4Rect & getRegion() const
int32_t GetHeight() const
bool _FastSolidCheck(int32_t x, int32_t y) const
static int32_t FastSolidCheckNextX(int32_t x)
int32_t GetBottom() const
void Value(const T &rStruct)
const char * getData() const