diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e8340ad9..fd605f3fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -299,6 +299,9 @@ set(OC_CLONK_SOURCES src/landscape/fow/C4FoWLightSection.h src/landscape/fow/C4FoWRegion.cpp src/landscape/fow/C4FoWRegion.h + src/landscape/fow/C4FoWDrawStrategy.cpp + src/landscape/fow/C4FoWDrawStrategy.h + src/landscape/fow/C4FoWBeamTriangle.h src/landscape/C4Landscape.cpp src/landscape/C4Landscape.h src/landscape/C4LandscapeRenderClassic.cpp diff --git a/src/landscape/fow/C4FoW.cpp b/src/landscape/fow/C4FoW.cpp index c678228b7..6c0a69120 100644 --- a/src/landscape/fow/C4FoW.cpp +++ b/src/landscape/fow/C4FoW.cpp @@ -4,9 +4,6 @@ #include -// TODO: Make sure to use int32_t throughout! - -//#define LIGHT_DEBUG C4FoW::C4FoW() : pLights(NULL) diff --git a/src/landscape/fow/C4FoWBeam.cpp b/src/landscape/fow/C4FoWBeam.cpp index ac5e06221..6cf47317b 100644 --- a/src/landscape/fow/C4FoWBeam.cpp +++ b/src/landscape/fow/C4FoWBeam.cpp @@ -25,7 +25,7 @@ StdStrBuf C4FoWBeam::getDesc() const { fDirty ? "*" : ""); } -bool C4FoWBeam::MergeRight(int x, int y) +bool C4FoWBeam::MergeRight(int32_t x, int32_t y) { // Note: Right-merging is the most common and most important optimization. // This procedure will probably be *hammered* as a result. Worth inlining? @@ -50,7 +50,7 @@ bool C4FoWBeam::MergeRight(int x, int y) return true; } -bool C4FoWBeam::MergeLeft(int x, int y) +bool C4FoWBeam::MergeLeft(int32_t x, int32_t y) { assert(!isDirty()); assert(isLeft(x, y)); @@ -70,7 +70,7 @@ bool C4FoWBeam::MergeLeft(int x, int y) return true; } -bool C4FoWBeam::EliminateRight(int x, int y) +bool C4FoWBeam::EliminateRight(int32_t x, int32_t y) { // Called on the beams left of the one getting eliminated C4FoWBeam *pElim = pNext, *pMerge = pNext->pNext; @@ -97,7 +97,7 @@ bool C4FoWBeam::EliminateRight(int x, int y) return true; } -C4FoWBeam *C4FoWBeam::Split(int x, int y) +C4FoWBeam *C4FoWBeam::Split(int32_t x, int32_t y) { // Make sure to never create negative-surface beams assert(isDirty()); assert(isInside(x, y)); @@ -137,7 +137,7 @@ void C4FoWBeam::MergeDirty() delete pWith; } -void C4FoWBeam::Clean(int y) +void C4FoWBeam::Clean(int32_t y) { // Search hit something, this beam is now clean. assert(isDirty()); @@ -146,7 +146,7 @@ void C4FoWBeam::Clean(int y) fDirty = false; } -void C4FoWBeam::Dirty(int y) +void C4FoWBeam::Dirty(int32_t y) { // Got invalidated, beam is dirty until updated iLeftEndY = y; diff --git a/src/landscape/fow/C4FoWBeam.h b/src/landscape/fow/C4FoWBeam.h index 98c1f3712..6c7a6b9c7 100644 --- a/src/landscape/fow/C4FoWBeam.h +++ b/src/landscape/fow/C4FoWBeam.h @@ -10,10 +10,9 @@ are the positions at which this beam did not hit but merely streaked a solid pixel (= the neighboring beam hits it) and thus continues until *EndX/Y. - It is assumed that the beam always goes up (in cartesian coordinate system, or down in display coordinate system), - thus the Y-position of the delimiting points is always > 0. - C4FoWLightSection transforms the coordinate system after calculation to implement the beams that go into other - directions. This class here always assumes one direction. + It is assumed that the beam always goes down in display coordinate system, thus the Y-position of the delimiting + points is always > 0. C4FoWLightSection transforms the coordinate system after calculation to implement the beams + that go into other directions. This class here always assumes one direction. A beam is initially marked as "dirty", meaning that the end points of the beam haven't been found yet (by the ray trace algorithm) and the beam will extend further. When all beams are "clean", the algorithm is done. @@ -58,47 +57,47 @@ public: StdStrBuf getDesc() const; /** returns whether the given point is left of an imaginery line drawn from the origin to the left delimiter point (point is left of beam) */ - bool isLeft(int x, int y) const { + bool isLeft(int32_t x, int32_t y) const { return iLeftX * y > x * iLeftY; } /** returns whether the given point is right of an imaginery line drawn from the origin to the right delimiter point (point is right of beam) */ - bool isRight(int x, int y) const { + bool isRight(int32_t x, int32_t 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 { + bool isInside(int32_t x, int32_t 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; } + void SetLeft(int32_t x, int32_t y) { iLeftX = x; iLeftY = y; } + void SetRight(int32_t x, int32_t 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 */ - bool MergeRight(int x, int y); + bool MergeRight(int32_t x, int32_t 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); + bool MergeLeft(int32_t x, int32_t 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) */ - C4FoWBeam *Split(int x, int y); + C4FoWBeam *Split(int32_t x, int32_t 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. In other words, removes the next beam and merges this beam with the next but one beam. Returns false and does not do the action in case the error threshold would be reached. */ - bool EliminateRight(int x, int y); + bool EliminateRight(int32_t x, int32_t y); void MergeDirty(); diff --git a/src/landscape/fow/C4FoWBeamTriangle.h b/src/landscape/fow/C4FoWBeamTriangle.h new file mode 100644 index 000000000..2fd88a875 --- /dev/null +++ b/src/landscape/fow/C4FoWBeamTriangle.h @@ -0,0 +1,26 @@ +#ifndef C4FOWBEAMTRIANGLE_H +#define C4FOWBEAMTRIANGLE_H + +/** A simple data structure holding data, mainly positions, about each beam + that is used for the rendering. The coordinates are global. +*/ +class C4FoWBeamTriangle +{ +public: + C4FoWBeamTriangle() : clipLeft(false), clipRight(false) {}; + + // whether this triangle is the last one in a row because the triangle + // that would normally be left/right of it is clipped + bool clipLeft, clipRight; + + float fanLX, fanLY, // left point of the triangle that has 100% light + fanRX, fanRY, // right point of the triangle that has 100% light + fadeLX, fadeLY, // left point of the quad in which the light fades out + fadeRY, fadeRX, // right point of the quad in which the light fades out + fadeIX, fadeIY; // intermediate fade point filling the space between the fade quads + + // whether the next triangle's edge is shorter than the edge of this one + bool descending; +}; + +#endif \ No newline at end of file diff --git a/src/landscape/fow/C4FoWDrawStrategy.cpp b/src/landscape/fow/C4FoWDrawStrategy.cpp new file mode 100644 index 000000000..7f142297d --- /dev/null +++ b/src/landscape/fow/C4FoWDrawStrategy.cpp @@ -0,0 +1,103 @@ + +#include "C4Include.h" +#include "C4FoWDrawStrategy.h" +#include "C4FoWLight.h" +#include "C4FoWRegion.h" +#include "C4DrawGL.h" + +const float C4FoWDrawLightTextureStrategy::C4FoWSmooth = 8.0; + +void C4FoWDrawLightTextureStrategy::Begin(int32_t passPar) +{ + pass = passPar; + + glShadeModel( GL_SMOOTH ); + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + + glBlendFunc(GL_ONE, GL_ONE); + if(pass == 0) + { + glBlendEquation( GL_FUNC_ADD ); + } + else if(pass == 1) + { + glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); + } + +} + +void C4FoWDrawLightTextureStrategy::End(int32_t pass) +{ + glBlendEquation( GL_FUNC_ADD ); +} + +void C4FoWDrawLightTextureStrategy::DrawVertex(float x, float y, bool shadeLight) +{ + if(pass == 0) + { + float dx = x - light->getX(); + float dy = y - light->getY(); + float dist = sqrt(dx*dx+dy*dy); + float mult = Min(0.5f / light->getSize(), 0.5f / dist); + float normX = (0.5f + dx * mult) / 1.5f / C4FoWSmooth; + float normY = (0.5f + dy * mult) / 1.5f / C4FoWSmooth; + if(shadeLight) glColor3f(0.5f/C4FoWSmooth, normX, normY); + else glColor3f(0.0f, normX, normY); + } + else + { + glColor3f(0.0f, 0.5f/1.5f/C4FoWSmooth, 0.5f/1.5f/C4FoWSmooth); + } + + // global coords -> region coords + x += -region->getRegion().x; + y += -region->getRegion().y; + + glVertex2f(x,y); +} + +void C4FoWDrawLightTextureStrategy::DrawDarkVertex(float x, float y) +{ + DrawVertex(x,y, false); +} + +void C4FoWDrawLightTextureStrategy::DrawLightVertex(float x, float y) +{ + DrawVertex(x,y, true); +} + +void C4FoWDrawWireframeStrategy::Begin(int32_t pass) +{ + glShadeModel( GL_SMOOTH ); + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); +} + +void C4FoWDrawWireframeStrategy::End(int32_t pass) +{ + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + glBlendEquation( GL_FUNC_ADD ); +} + +void C4FoWDrawWireframeStrategy::DrawVertex(float x, float y) +{ + // global coords -> screen pos and zoom + x += screen->X - screen->TargetX; + y += screen->Y - screen->TargetY; + pGL->ApplyZoom(x,y); + glVertex2f(x,y); +} + +void C4FoWDrawWireframeStrategy::DrawDarkVertex(float x, float y) +{ + if(!draw) return; + glColor3f(0.5f, 0.5f, 0.0f); + DrawVertex(x, y); +} + +void C4FoWDrawWireframeStrategy::DrawLightVertex(float x, float y) +{ + if(!draw) return; + glColor3f(1.0f, 0.0f, 0.0f); + DrawVertex(x, y); +} + diff --git a/src/landscape/fow/C4FoWDrawStrategy.h b/src/landscape/fow/C4FoWDrawStrategy.h new file mode 100644 index 000000000..0ef6721fc --- /dev/null +++ b/src/landscape/fow/C4FoWDrawStrategy.h @@ -0,0 +1,97 @@ +#ifndef C4FOWDRAWSTRATEGY_H +#define C4FOWDRAWSTRATEGY_H + +#include "C4DrawGL.h" +#include + +class C4FoWRegion; +class C4TargetFacet; +class C4FoWLight; + +/** A C4FoWDrawStrategy is a connector to OpenGL calls used to draw the light. + C4FoWLight tells this class which part of the light should be drawn now + and subsequently pushes the vertices with the information whether a vertex + is light or dark. + + This class is an abstract base class, it is up to the implementing classes + to actually draw anything here.*/ +class C4FoWDrawStrategy +{ +public: + virtual ~C4FoWDrawStrategy() {}; + + /** Returns in how many rendering passes the light should be rendered */ + virtual int32_t GetRequestedPasses() { return 1; }; + /** Called before each rendering pass */ + virtual void Begin(int32_t pass) = 0; + /** Called after each rendering pass */ + virtual void End(int32_t pass) = 0; + + virtual void DrawLightVertex(float x, float y) = 0; + virtual void DrawDarkVertex(float x, float y) = 0; + + /** Called before rendering the inner triangle fan (the area with 100% light) */ + virtual void BeginFan() { glBegin(GL_TRIANGLE_FAN); }; + /** Called after rendering the inner triangle fan */ + virtual void EndFan() { glEnd(); }; + + /** Called before rendering the quads in which the light fades out */ + virtual void BeginFade() { glBegin(GL_QUADS); }; + /** Called after rendering the quads in which the light fades out */ + virtual void EndFade() { glEnd(); }; + + /** Called before rendering the triangles that fill the space between the fadeout quads */ + virtual void BeginIntermediateFade() { glBegin(GL_TRIANGLES); }; + /** Called after rendering the triangles that fill the space between the fadeout quads */ + virtual void EndIntermediateFade() { glEnd(); }; +}; + +/** This draw strategy is the default draw strategy that draws the light + onto the given region. */ +class C4FoWDrawLightTextureStrategy : public C4FoWDrawStrategy +{ +public: + C4FoWDrawLightTextureStrategy(const C4FoWLight* light, const C4FoWRegion* region) : light(light), region(region) {}; + + virtual int32_t GetRequestedPasses() { return 2; }; + virtual void DrawLightVertex(float x, float y); + virtual void DrawDarkVertex(float x, float y); + virtual void Begin(int32_t pass); + virtual void End(int32_t pass); + +private: + void DrawVertex(float x, float y, bool shadeLight); + + static const float C4FoWSmooth; + + const C4FoWLight* light; + const C4FoWRegion* region; + + int32_t pass; +}; + +/** This draw strategy is the debug draw strategy (press Ctrl+F7,...) that + draws a wireframe of the light triangles (except the inner fan, but you can + change that in the code below) directly onto the screen. */ +class C4FoWDrawWireframeStrategy : public C4FoWDrawStrategy +{ +public: + C4FoWDrawWireframeStrategy(const C4FoWLight* light, const C4TargetFacet *screen) : light(light), screen(screen), draw(true) {}; + + virtual void DrawLightVertex(float x, float y); + virtual void DrawDarkVertex(float x, float y); + virtual void Begin(int32_t pass); + virtual void End(int32_t pass); + + virtual void BeginFan() { C4FoWDrawStrategy::BeginFan(); draw = false; }; + virtual void EndFan() { C4FoWDrawStrategy::EndFan(); draw = true; }; + +private: + void C4FoWDrawWireframeStrategy::DrawVertex(float x, float y); + + const C4FoWLight* light; + const C4TargetFacet* screen; + bool draw; +}; + +#endif diff --git a/src/landscape/fow/C4FoWLight.cpp b/src/landscape/fow/C4FoWLight.cpp index 59d0d6231..7f4334d1a 100644 --- a/src/landscape/fow/C4FoWLight.cpp +++ b/src/landscape/fow/C4FoWLight.cpp @@ -2,33 +2,36 @@ #include "C4Include.h" #include "C4FoWLight.h" #include "C4FoWLightSection.h" +#include "C4FoWBeamTriangle.h" +#include "C4FoWDrawStrategy.h" C4FoWLight::C4FoWLight(C4Object *pObj) - : iX(fixtoi(pObj->fix_x)), iY(fixtoi(pObj->fix_y)), - iReach(pObj->PlrViewRange), iFadeout(50), iSize(20), - pNext(NULL), pObj(pObj) + : iX(fixtoi(pObj->fix_x)), + iY(fixtoi(pObj->fix_y)), + iReach(pObj->PlrViewRange), + iFadeout(50), + iSize(20), + pNext(NULL), + pObj(pObj), + sectionUp(this, 0), + sectionLeft(this, 270), + sectionDown(this, 180), + sectionRight(this, 90) { - pSections = new C4FoWLightSection(this, 0); - pSections = new C4FoWLightSection(this, 90, pSections); - pSections = new C4FoWLightSection(this, 180, pSections); - pSections = new C4FoWLightSection(this, 270, pSections); } C4FoWLight::~C4FoWLight() { - while (C4FoWLightSection *pSect = pSections) { - pSections = pSect->getNext(); - delete pSect; - } } void C4FoWLight::Invalidate(C4Rect r) { - for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) - pSect->Invalidate(r); + sectionUp.Invalidate(r); + sectionDown.Invalidate(r); + sectionLeft.Invalidate(r); + sectionRight.Invalidate(r); } - void C4FoWLight::SetReach(int32_t iReach2, int32_t iFadeout2) { // Fadeout changes don't matter @@ -37,17 +40,22 @@ void C4FoWLight::SetReach(int32_t iReach2, int32_t iFadeout2) if (iReach == iReach2) return; // Reach decreased? Prune beams - if (iReach2 < iReach) { + if (iReach2 < iReach) + { iReach = iReach2; - for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) - pSect->Prune(iReach); + sectionUp.Prune(iReach); + sectionDown.Prune(iReach); + sectionLeft.Prune(iReach); + sectionRight.Prune(iReach); } else { // Reach increased? Dirty beams that might get longer now iReach = iReach2; - for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) - pSect->Dirty(iReach); + sectionUp.Dirty(iReach); + sectionDown.Dirty(iReach); + sectionLeft.Dirty(iReach); + sectionRight.Dirty(iReach); } } @@ -58,20 +66,201 @@ void C4FoWLight::Update(C4Rect Rec) int32_t iNX = fixtoi(pObj->fix_x), iNY = fixtoi(pObj->fix_y); if (iNX != iX || iNY != iY) { - for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) - // pruning to zero length results in the beam being cleared and new ones created - pSect->Prune(0); + sectionUp.Prune(0); + sectionDown.Prune(0); + sectionLeft.Prune(0); + sectionRight.Prune(0); iX = iNX; iY = iNY; } - for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) - pSect->Update(Rec); - + sectionUp.Update(Rec); + sectionDown.Update(Rec); + sectionLeft.Update(Rec); + sectionRight.Update(Rec); } -void C4FoWLight::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen) +void C4FoWLight::Render(C4FoWRegion *region, const C4TargetFacet *onScreen) { - for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext()) - pSect->Render(pRegion, pOnScreen); + std::list triangles; + triangles.splice(triangles.end(), sectionUp.CalculateTriangles(region)); + triangles.splice(triangles.end(), sectionRight.CalculateTriangles(region)); + triangles.splice(triangles.end(), sectionDown.CalculateTriangles(region)); + triangles.splice(triangles.end(), sectionLeft.CalculateTriangles(region)); + + CalculateIntermediateFadeTriangles(triangles); + + // Here's the master plan for updating the lights texture. We + // want to add intensity (R channel) as well as the normal (GB channels). + // Normals are obviously meant to be though of as signed, though, + // so the equation we want would be something like + // + // R_new = BoundBy(R_old + R, 0.0, 1.0) + // G_new = BoundBy(G_old + G - 0.5, 0.0, 1.0) + // B_new = BoundBy(B_old + B - 0.5, 0.0, 1.0) + // + // It seems we can't get that directly though - glBlendFunc only talks + // about two operands. Even if we make two passes, we have to take + // care that that we don't over- or underflow in the intermediate pass. + // + // Therefore, we store G/1.5 instead of G, losing a bit of accuracy, + // but allowing us to formulate the following approximation without + // overflows: + // + // G_new = BoundBy(BoundBy(G_old + G / 1.5), 0.0, 1.0) - 0.5 / 1.5, 0.0, 1.0) + // B_new = BoundBy(BoundBy(B_old + B / 1.5), 0.0, 1.0) - 0.5 / 1.5, 0.0, 1.0) + + C4FoWDrawStrategy* pen; + if (onScreen) pen = new C4FoWDrawWireframeStrategy(this, onScreen); + else pen = new C4FoWDrawLightTextureStrategy(this, region); + + for(int pass = 0; pass < pen->GetRequestedPasses(); pass++) + { + pen->Begin(pass); + + DrawFan(pen, triangles); + DrawFade(pen, triangles); + DrawIntermediateFadeTriangles(pen, triangles); + + pen->End(pass); + } + + delete pen; } +void C4FoWLight::CalculateIntermediateFadeTriangles(std::list &triangles) +{ + for (std::list::iterator it = triangles.begin(), nextIt = it; it != triangles.end(); ++it) + { + // wrap around + ++nextIt; + if(nextIt == triangles.end()) nextIt = triangles.begin(); + + C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt; // just for convenience + + // don't calculate if it should not be drawn anyway + if (tri.clipRight || nextTri.clipLeft) continue; + + // Midpoint + tri.fadeIX = (tri.fadeRX + nextTri.fadeLX) / 2; + tri.fadeIY = (tri.fadeRY + nextTri.fadeLY) / 2; + + float distFanR = GetSquaredDistanceTo(tri.fanRX, tri.fanRY); + float distNextFanL = GetSquaredDistanceTo(nextTri.fanLX, nextTri.fanLY); + float distFadeI = GetSquaredDistanceTo(tri.fadeIX, tri.fadeIY); + + // an intermediate fade point is not necessery in all cases + tri.descending = distFanR > distNextFanL; + if (tri.descending) { + float distFadeR = GetSquaredDistanceTo(tri.fadeRX, tri.fadeRY); + if (distFadeR < distFadeI) + { + tri.fadeIX = nextTri.fadeLX; + tri.fadeIY = nextTri.fadeLY; + } + } + else + { + float distNextFadeL = GetSquaredDistanceTo(nextTri.fadeLX, nextTri.fadeLY); + if (nextTri.fadeLY < tri.fadeIY) + { + tri.fadeIX = tri.fadeRX; + tri.fadeIY = tri.fadeRY; + } + } + } +} + +void C4FoWLight::DrawFan(C4FoWDrawStrategy* pen, std::list &triangles) +{ + pen->BeginFan(); + pen->DrawLightVertex(getX(), getY()); + + for (std::list::iterator it = triangles.begin(), nextIt = it; it != triangles.end(); ++it) + { + // wrap around + ++nextIt; + if(nextIt == triangles.end()) nextIt = triangles.begin(); + + C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt; // just for convenience + + pen->DrawLightVertex(tri.fanLX, tri.fanLY); + pen->DrawLightVertex(tri.fanRX, tri.fanRY); + } + pen->EndFan(); +} + +void C4FoWLight::DrawFade(C4FoWDrawStrategy* pen, std::list &triangles) +{ + pen->BeginFade(); + + for (std::list::iterator it = triangles.begin(); it != triangles.end(); ++it) + { + C4FoWBeamTriangle &tri = *it; // just for convenience + + // The quad will be empty if fan points match + if (tri.fanLX == tri.fanRX && tri.fanLY == tri.fanRY) continue; + + pen->DrawLightVertex(tri.fanLX, tri.fanLY); + pen->DrawLightVertex(tri.fanRX, tri.fanRY); + pen->DrawDarkVertex(tri.fadeRX, tri.fadeRY); + pen->DrawDarkVertex(tri.fadeLX, tri.fadeLY); + } + pen->EndFade(); +} + +void C4FoWLight::DrawIntermediateFadeTriangles(C4FoWDrawStrategy* pen, std::list &triangles) +{ + pen->BeginIntermediateFade(); + + for (std::list::iterator it = triangles.begin(), nextIt = it; it != triangles.end(); ++it) + { + // wrap around + ++nextIt; + if(nextIt == triangles.end()) nextIt = triangles.begin(); + + C4FoWBeamTriangle &tri = *it, &nextTri = *nextIt; // just for convenience + + // no inter-fade triangles when it should be clipped + if (tri.clipRight || nextTri.clipLeft) continue; + + if (tri.descending) { + + // Lower fade triangle + pen->DrawLightVertex(tri.fanRX, tri.fanRY); + pen->DrawDarkVertex(tri.fadeIX, tri.fadeIY); + pen->DrawDarkVertex(tri.fadeRX, tri.fadeRY); + + // Intermediate fade triangle, if necessary + if (tri.fadeIY != nextTri.fadeRY || tri.fadeIX != nextTri.fadeRX) { + pen->DrawLightVertex(tri.fanRX, tri.fanRY); + pen->DrawDarkVertex(nextTri.fadeLX, nextTri.fadeLY); + pen->DrawDarkVertex(tri.fadeIX, tri.fadeIY); + } + + // Upper fade triangle + pen->DrawLightVertex(tri.fanRX, tri.fanRY); + pen->DrawLightVertex(nextTri.fanLX, nextTri.fanLY); + pen->DrawDarkVertex(nextTri.fadeLX, nextTri.fadeLY); + + } else { + + // Lower fade triangle + pen->DrawLightVertex(nextTri.fanLX, nextTri.fanLY); + pen->DrawDarkVertex(nextTri.fadeLX, nextTri.fadeLY); + pen->DrawDarkVertex(tri.fadeIX, tri.fadeIY); + + // Intermediate fade triangle, if necessary + if (tri.fadeIY != nextTri.fadeRY || tri.fadeIX != nextTri.fadeRX) { + pen->DrawLightVertex(nextTri.fanLX, nextTri.fanLY); + pen->DrawDarkVertex(tri.fadeIX, tri.fadeIY); + pen->DrawDarkVertex(tri.fadeRX, tri.fadeRY); + } + + // Upper fade triangle + pen->DrawLightVertex(nextTri.fanLX, nextTri.fanLY); + pen->DrawDarkVertex(tri.fadeRX, tri.fadeRY); + pen->DrawLightVertex(tri.fanRX, tri.fanRY); + } + } + pen->EndIntermediateFade(); +} diff --git a/src/landscape/fow/C4FoWLight.h b/src/landscape/fow/C4FoWLight.h index 900cbda72..5ac44a50c 100644 --- a/src/landscape/fow/C4FoWLight.h +++ b/src/landscape/fow/C4FoWLight.h @@ -2,9 +2,10 @@ #define C4FOWLIGHT_H #include "C4Object.h" -#include "C4Rect.h" #include "C4Surface.h" #include "C4FacetEx.h" +#include "C4FoWLightSection.h" +#include "C4Rect.h" /** 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 beams for each direction (up, down, left, right). @@ -23,16 +24,19 @@ private: int32_t iReach; // maximum length of beams int32_t iFadeout; // number of pixels over which beams fade out int32_t iSize; // size of the light source. Decides smoothness of shadows - class C4FoWLightSection *pSections; C4FoWLight *pNext; C4Object *pObj; // Associated object + C4FoWLightSection sectionUp; + C4FoWLightSection sectionLeft; + C4FoWLightSection sectionDown; + C4FoWLightSection sectionRight; + public: int32_t getX() const { return iX; } int32_t getY() const { return iY; } int32_t getReach() const { return iReach; } int32_t getFadeout() const { return iFadeout; } - // 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 getSize() const { return iSize; } C4FoWLight *getNext() const { return pNext; } @@ -46,9 +50,23 @@ public: void Invalidate(C4Rect r); /** Update all light beams within the given rectangle for this light */ void Update(C4Rect r); - + /** Render this light*/ void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL); +private: + /** Calculate the intermediate fade points used for constructing the intermediate fade triangles later on */ + void CalculateIntermediateFadeTriangles(std::list &triangles); + + /** Draws the triangle fan (the area with 100% light around the light source) with the given strategy */ + void DrawFan(class C4FoWDrawStrategy* pen, std::list &triangles); + /** Draws the fadeoot triangles - those around the triangle fan - with the given strategy */ + void DrawFade(C4FoWDrawStrategy* pen, std::list &triangles); + /** Draws the fadeout triangles in between the normal fadeout triangles with the given strategy */ + void DrawIntermediateFadeTriangles(C4FoWDrawStrategy* pen, std::list &triangles); + /** Returns the (squared) distance from this light source to the given point. Squared simply because we only need this + for comparison of distances. So we don't bother to sqrt it */ + float GetSquaredDistanceTo(int32_t x, int32_t y) { return (x - getX()) * (x - getX()) + (y - getY()) * (y - getY()); } + }; #endif \ No newline at end of file diff --git a/src/landscape/fow/C4FoWLightSection.cpp b/src/landscape/fow/C4FoWLightSection.cpp index edaabde9d..d7eb6c4e5 100644 --- a/src/landscape/fow/C4FoWLightSection.cpp +++ b/src/landscape/fow/C4FoWLightSection.cpp @@ -1,11 +1,11 @@ #include "C4Include.h" #include "C4FoWLightSection.h" +#include "C4FoWBeamTriangle.h" #include "C4FoWBeam.h" #include "C4FoWLight.h" #include "C4FoWRegion.h" #include "C4Landscape.h" -#include "C4DrawGL.h" #include "float.h" @@ -44,11 +44,7 @@ bool find_cross(float x1, float y1, float x2, float y2, return true; } -const float C4FoWSmooth = 8.0; - - -C4FoWLightSection::C4FoWLightSection(C4FoWLight *pLight, int r, C4FoWLightSection *pNext) - : pLight(pLight), iRot(r), pNext(pNext) +C4FoWLightSection::C4FoWLightSection(C4FoWLight *pLight, int r) : pLight(pLight), iRot(r) { // Rotation matrices iRot = r % 360; @@ -81,6 +77,20 @@ C4FoWLightSection::~C4FoWLightSection() ClearBeams(); } +inline void C4FoWLightSection::LightBallExtremePoint(float x, float y, float dir, float &lightX, float &lightY) const +{ + float d = sqrt(x * x + y * y); + float s = Min(float(pLight->getSize()), d / 5.0f); + lightX = dir * y * s / d; + lightY = dir * -x * s / d; +} + +inline void C4FoWLightSection::LightBallRightMostPoint(float x, float y, float &lightX, float &lightY) const + { LightBallExtremePoint(x,y,+1.0f,lightX,lightY); } + +inline void C4FoWLightSection::LightBallLeftMostPoint(float x, float y, float &lightX, float &lightY) const + { LightBallExtremePoint(x,y,-1.0f,lightX,lightY); } + void C4FoWLightSection::ClearBeams() { while (C4FoWBeam *pBeam = pBeams) { @@ -96,7 +106,7 @@ void C4FoWLightSection::Prune(int32_t iReach) pBeams = new C4FoWBeam(-1, 1, 1, 1); return; } - // TODO: Merge active beams that we have pruned to same length + // TODO PeterW: Merge active beams that we have pruned to same length for (C4FoWBeam *pBeam = pBeams; pBeam; pBeam = pBeam->getNext()) pBeam->Prune(iReach); } @@ -232,7 +242,7 @@ void C4FoWLightSection::Update(C4Rect RectIn) // Do a scan int32_t xl = Max(pBeam->getLeftX(y), Bounds.x), xr = Min(pBeam->getRightX(y), Bounds.x+Bounds.Wdt-1); - for(int x = xl; x <= xr; x++) { + for(int32_t x = xl; x <= xr; x++) { // Fast free? if (!Landscape._FastSolidCheck(transX(x,y), transY(x,y))) @@ -246,7 +256,7 @@ void C4FoWLightSection::Update(C4Rect RectIn) if (!GBackSolid(transX(x,y), transY(x,y))) continue; // Split points - int x1 = x - 1, x2 = x + 1; + int32_t x1 = x - 1, x2 = x + 1; bool fSplitLeft = !pBeam->isLeft(x1, y); bool fSplitRight = !pBeam->isRight(x2, y); @@ -395,12 +405,14 @@ int32_t C4FoWLightSection::FindBeamsClipped(const C4Rect &pInRect, C4FoWBeam *&p return iBeamCount; } -void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen) +std::list C4FoWLightSection::CalculateTriangles(C4FoWRegion *pRegion) { C4FoWBeam *pStart = NULL, *pEnd = NULL; int32_t iBeamCount = FindBeamsClipped(rtransRect(pRegion->getRegion()), pStart, pEnd); // no beams inside the rectangle? Good, nothing to render - if(!iBeamCount) return; + std::list result; + if(!iBeamCount) return result; + int32_t iOriginalBeamCount = iBeamCount; // Allocate array for our points (lots of them) @@ -411,9 +423,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr *gFadeLX = gFanRY + iBeamCount, *gFadeLY = gFadeLX + iBeamCount, *gFadeRX = gFadeLY + iBeamCount, - *gFadeRY = gFadeRX + iBeamCount, - *gFadeIX = gFadeRY + iBeamCount, - *gFadeIY = gFadeIX + iBeamCount; + *gFadeRY = gFadeRX + iBeamCount; int32_t i; C4FoWBeam *pBeam = pStart; for (i = 0, pBeam = pStart; i < iBeamCount; i++, pBeam = pBeam->getNext()) { @@ -447,16 +457,6 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr if(Min(gFanRY[i], gFanLY[i+1]) != gBestLevel) continue; - // Debugging - // #define FAN_STEP_DEBUG -#ifdef FAN_STEP_DEBUG - LogSilentF("Fan step %d (i=%d)", iStep, i); - for (j = 0; j < iBeamCnt; j++) { - LogSilentF(" %.02f %.02f", gFanLX[j], gFanLY[j]); - LogSilentF(" %.02f %.02f", gFanRX[j], gFanRY[j]); - } -#endif - // Calculate light bounds. We assume a "smaller" light for closer beams float gLightLX, gLightLY, gLightRX, gLightRY; LightBallLeftMostPoint(gFanRX[i], gFanRY[i], gLightLX, gLightLY); @@ -472,7 +472,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr if ( (gFanRY[i] - gFanLY[i]) * (gFanLX[i+1] - gLightRX) >= (gFanRX[i] - gFanLX[i]) * (gFanLY[i+1] - gLightRY)) { - // Reduce to upper point (Yep, we now that the upper point + // Reduce to upper point (Yep, we know that the upper point // must be the right one. Try to figure out why!) assert(gFanRY[i] <= gFanLY[i]); gFanLX[i] = gFanRX[i]; @@ -634,9 +634,6 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr } // end for (int iStep = 0; iStep < 100000; iStep++) loop // Phase 2: Calculate fade points -#ifdef FAN_STEP_DEBUG - LogSilent("Fade points"); -#endif // FAN_STEP_DEBUG for (i = 0; i < iBeamCount; i++) { // Calculate light bounds. Note that the way light size is calculated @@ -650,7 +647,8 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr // right-most light point. // For once we actually calculate this using the real distance - float dx = gFanLX[i] - gLightLX, dy = gFanLY[i] - gLightLY; + float dx = gFanLX[i] - gLightLX; + float dy = gFanLY[i] - gLightLY; float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy); gFadeLX[i] = gFanLX[i] + d * dx; gFadeLY[i] = gFanLY[i] + d * dy; @@ -671,306 +669,37 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr } -#ifdef FAN_STEP_DEBUG - LogSilentF(" %.02f %.02f", gFadeLX[i], gFadeLY[i]); - LogSilentF(" %.02f %.02f", gFadeRX[i], gFadeRY[i]); -#endif } - // Phase 3: Calculate intermediate fade point -#ifdef FAN_STEP_DEBUG - LogSilent("Intermediate points"); -#endif // FAN_STEP_DEBUG -#define NEWER_INTER_FADE_CODE -//#define NEW_INTER_FADE_CODE -#ifdef NEWER_INTER_FADE_CODE - pBeam = pStart; -#endif - bool *fAscend = new bool[iBeamCount]; - for (i = 0; i+1 < iBeamCount; i++) { - - // Calculate light bounds. We assume a "smaller" light for closer beams - float gLightLX, gLightLY, gLightRX, gLightRY; - LightBallLeftMostPoint(gFanLX[i+1], gFanLY[i+1], gLightRX, gLightRY); - LightBallRightMostPoint(gFanRX[i], gFanRY[i], gLightLX, gLightLY); - -#ifdef NEWER_INTER_FADE_CODE - // Midpoint - float mx = (gFadeRX[i] + gFadeLX[i+1]) / 2, - my = (gFadeRY[i] + gFadeLY[i+1]) / 2; - while (pBeam->getNext() && pBeam->isRight(mx, my)) - pBeam = pBeam->getNext(); -#endif - - // Ascending? - fAscend[i] = gFanRY[i] > gFanLY[i+1]; - if (gFanRY[i] > gFanLY[i+1]) { - -#ifdef NEWER_INTER_FADE_CODE - - float dx, dy; - find_cross(0,0, mx, my, - pBeam->getLeftEndXf(), pBeam->getLeftEndY(), pBeam->getRightEndXf(), pBeam->getRightEndY(), - &dx, &dy); - float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy); - gFadeIX[i] = mx + d * dx; - gFadeIY[i] = my + d * dy; - if (gFadeRY[i] < gFadeIY[i]) { - gFadeIX[i] = gFadeLX[i+1]; - gFadeIY[i] = gFadeLY[i+1]; - } - -#elif defined(NEW_INTER_FADE_CODE) - - // Fade intermediate point is on the left side - float dx = gFanRX[i] - gLightLX; - float dy = gFanRY[i] - gLightLY; - float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy); - gFadeIX[i] = gFanRX[i] + d * dx; - gFadeIY[i] = gFanRY[i] + d * dy; - -#else - // Fade intermediate point is on the right side - gFadeIX[i] = gFadeLX[i+1]; - gFadeIY[i] = gFadeLY[i+1]; - - // Project on left beam's height where necessary - if (gFadeIY[i] < gFanRY[i]) { - float d = (gFanRY[i] - gFadeIY[i]) / (gFadeIY[i] - gLightY); - gFadeIX[i] += d * (gFadeIX[i] - gLightRX); - gFadeIY[i] = gFanRY[i]; - } -#endif - - // Descending? - } else { - -#ifdef NEWER_INTER_FADE_CODE - - float dx, dy; - find_cross(0,0, mx,my, - pBeam->getLeftEndXf(), pBeam->getLeftEndY(), pBeam->getRightEndXf(), pBeam->getRightEndY(), - &dx, &dy); - float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy) / 2; - gFadeIX[i] = mx + d * dx; - gFadeIY[i] = my + d * dy; - if (gFadeLY[i+1] < gFadeIY[i]) { - gFadeIX[i] = gFadeRX[i]; - gFadeIY[i] = gFadeRY[i]; - } - -#elif defined(NEW_INTER_FADE_CODE) - - // Fade intermediate point is on the right side - float dx = gFanLX[i+1] - gLightRX; - float dy = gFanLY[i+1] - gLightRY; - float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy); - gFadeIX[i] = gFanLX[i+1] + d * dx; - gFadeIY[i] = gFanLY[i+1] + d * dy; - -#else - // Fade intermediate point is on the left side - gFadeIX[i] = gFadeRX[i]; - gFadeIY[i] = gFadeRY[i]; - - // Project on right beam's height where necessary - if (gFadeIY[i] < gFanLY[i+1]) { - float d = (gFanLY[i+1] - gFadeIY[i]) / (gFadeIY[i] - gLightY); - gFadeIX[i] += d * (gFadeIX[i] - gLightLX); - gFadeIY[i] = gFanLY[i+1]; - } -#endif - - } - -#ifdef FAN_STEP_DEBUG - LogSilentF(" %.02f %.02f", gFadeIX[i], gFadeIY[i]); -#endif // FAN_STEP_DEBUG - } - - // Phase 4: Transform all points into region coordinates - for (i = 0; i < 5; i++) { + // Phase 4: Transform all points into global coordinates + for (i = 0; i < 4; i++) { float *pX = gFanLX + 2 * i * iOriginalBeamCount, *pY = gFanLY + 2 * i * iOriginalBeamCount; for (int32_t j = 0; j < iBeamCount; j++) { float x = pX[j], y = pY[j]; - if (pOnScreen) - { - pX[j] = float(pOnScreen->X) + transX(x, y) - pOnScreen->TargetX, - pY[j] = float(pOnScreen->Y) + transY(x, y) - pOnScreen->TargetY; - pGL->ApplyZoom(pX[j], pY[j]); - } - else - { - pX[j] = transX(x, y) - pRegion->getRegion().x; - pY[j] = transY(x, y) - pRegion->getRegion().y; - } + pX[j] = transX(x, y); + pY[j] = transY(x, y); } } - // Calculate position of the light in the buffer - float gLightX = transX(0,0) - pRegion->getRegion().x, - gLightY = transY(0,0) - pRegion->getRegion().y; - - // Here's the master plan for updating the lights texture. We - // want to add intensity (R channel) as well as the normal (GB channels). - // Normals are obviously meant to be though of as signed, though, - // so the equation we want would be something like - // - // R_new = BoundBy(R_old + R, 0.0, 1.0) - // G_new = BoundBy(G_old + G - 0.5, 0.0, 1.0) - // B_new = BoundBy(B_old + B - 0.5, 0.0, 1.0) - // - // It seems we can't get that directly though - glBlendFunc only talks - // about two operands. Even if we make two passes, we have to take - // care that that we don't over- or underflow in the intermediate pass. - // - // Therefore, we store G/1.5 instead of G, losing a bit of accuracy, - // but allowing us to formulate the following approximation without - // overflows: - // - // G_new = BoundBy(BoundBy(G_old + G / 1.5), 0.0, 1.0) - 0.5 / 1.5, 0.0, 1.0) - // B_new = BoundBy(BoundBy(B_old + B / 1.5), 0.0, 1.0) - 0.5 / 1.5, 0.0, 1.0) - - // Two passes - for(int iPass = 0; iPass < (pOnScreen ? 1 : 2); iPass++) { - - // Pass 2: Subtract - if (!pOnScreen && iPass == 1) { - glBlendEquation(GL_FUNC_REVERSE_SUBTRACT); - glBlendFunc(GL_ONE, GL_ONE); - } - - // Help me! My brain can't program without local function definitions anymore! - #define VERTEX(x,y,light) \ - if(pOnScreen) { \ - if(light) glColor3f(1.0f, 0.0f, 0.0f); \ - else glColor3f(0.5f, 0.5f, 0.0f); \ - } else if(iPass == 0) { \ - float dx = (x) - gLightX, dy = (y) - gLightY; \ - float gDist = sqrt(dx*dx+dy*dy); \ - float gMult = Min(0.5f / pLight->getSize(), 0.5f / gDist); \ - float gNormX = (0.5f + dx * gMult) / 1.5f / C4FoWSmooth; \ - float gNormY = (0.5f + dy * gMult) / 1.5f / C4FoWSmooth; \ - if(light) glColor3f(0.5f/C4FoWSmooth, gNormX, gNormY); \ - else glColor3f(0.0f, gNormX, gNormY); \ - } else glColor3f(0.0f, 0.5f/1.5f/C4FoWSmooth, 0.5f/1.5f/C4FoWSmooth); \ - glVertex2f(x,y) - #define DARK(x,y) VERTEX(x,y,false) - #define LIGHT(x,y) VERTEX(x,y,true) - #define BEGIN_TRIANGLE \ - if(pOnScreen) glBegin(GL_LINE_LOOP) - #define END_TRIANGLE \ - if(pOnScreen) glEnd() - - // Draw the fan - glShadeModel(GL_SMOOTH); - glBegin(pOnScreen ? GL_LINE_STRIP : GL_TRIANGLE_FAN); - if (!pOnScreen) { - LIGHT(gLightX, gLightY); - } - for (i = 0; i < iBeamCount; i++) { - if (i == 0 || gFanRX[i-1] != gFanLX[i] || gFanRY[i-1] != gFanLY[i]) { - LIGHT(gFanLX[i], gFanLY[i]); - } - if (gFanLX[i] != gFanRX[i] || gFanLY[i] != gFanRY[i]) { - LIGHT(gFanRX[i], gFanRY[i]); - } - } - glEnd(); - - // Draw the fade - glShadeModel(GL_SMOOTH); - if(!pOnScreen) glBegin(GL_TRIANGLES); - - for (i = 0; i < iBeamCount; i++) { - - // The quad. Will be empty if fan points match - if (gFanLX[i] != gFanRX[i] || gFanLY[i] != gFanRY[i]) { - - // upper triangle - BEGIN_TRIANGLE; - LIGHT(gFanLX[i], gFanLY[i]); - LIGHT(gFanRX[i], gFanRY[i]); - DARK(gFadeLX[i], gFadeLY[i]); - END_TRIANGLE; - - // lower triangle, if necessary - if (gFadeLX[i] != gFadeRX[i] || gFadeLY[i] != gFadeRY[i]) { - BEGIN_TRIANGLE; - LIGHT(gFanRX[i], gFanRY[i]); - DARK(gFadeRX[i], gFadeRY[i]); - DARK(gFadeLX[i], gFadeLY[i]); - END_TRIANGLE; - } - } - - // No intermediate fade for last point - if (i+1 >= iBeamCount) continue; - - // Ascending? - if (fAscend[i]) { - - // Lower fade triangle - BEGIN_TRIANGLE; - LIGHT(gFanRX[i], gFanRY[i]); - DARK(gFadeIX[i], gFadeIY[i]); - DARK(gFadeRX[i], gFadeRY[i]); - END_TRIANGLE; - - // Intermediate fade triangle, if necessary - if (gFadeIY[i] != gFadeLY[i+1]) { - BEGIN_TRIANGLE; - LIGHT(gFanRX[i], gFanRY[i]); - DARK(gFadeLX[i+1], gFadeLY[i+1]); - DARK(gFadeIX[i], gFadeIY[i]); - END_TRIANGLE; - } - - // Upper fade triangle - BEGIN_TRIANGLE; - LIGHT(gFanRX[i], gFanRY[i]); - LIGHT(gFanLX[i+1], gFanLY[i+1]); - DARK(gFadeLX[i+1], gFadeLY[i+1]); - END_TRIANGLE; - - // Descending? - } else { - - // Lower fade triangle - BEGIN_TRIANGLE; - LIGHT(gFanLX[i+1], gFanLY[i+1]); - DARK(gFadeLX[i+1], gFadeLY[i+1]); - DARK(gFadeIX[i], gFadeIY[i]); - END_TRIANGLE; - - // Intermediate fade triangle, if necessary - if (gFadeIY[i] != gFadeRY[i]) { - BEGIN_TRIANGLE; - LIGHT(gFanLX[i+1], gFanLY[i+1]); - DARK(gFadeIX[i], gFadeIY[i]); - DARK(gFadeRX[i], gFadeRY[i]); - END_TRIANGLE; - } - - // Upper fade triangle - BEGIN_TRIANGLE; - LIGHT(gFanLX[i+1], gFanLY[i+1]); - DARK(gFadeRX[i], gFadeRY[i]); - LIGHT(gFanRX[i], gFanRY[i]); - END_TRIANGLE; - - } - } - if (!pOnScreen) - glEnd(); // GL_TRIANGLES + for (i = 0; i < iBeamCount; i++) + { + C4FoWBeamTriangle triangle = C4FoWBeamTriangle(); + triangle.fanLX = gFanLX[i]; + triangle.fanLY = gFanLY[i]; + triangle.fanRX = gFanRX[i]; + triangle.fanRY = gFanRY[i]; + triangle.fadeLX = gFadeLX[i]; + triangle.fadeLY = gFadeLY[i]; + triangle.fadeRX = gFadeRX[i]; + triangle.fadeRY = gFadeRY[i]; + triangle.clipLeft = false; // TODO Newton: pBeams.start != pStart + triangle.clipRight = false; // TODO Newton: pBeams.end != pEnd + result.push_back(triangle); } delete[] gFanLX; - delete[] fAscend; - - // Reset GL state - glBlendEquation(GL_FUNC_ADD); + return result; } diff --git a/src/landscape/fow/C4FoWLightSection.h b/src/landscape/fow/C4FoWLightSection.h index ff24eb62b..43e88ed46 100644 --- a/src/landscape/fow/C4FoWLightSection.h +++ b/src/landscape/fow/C4FoWLightSection.h @@ -2,10 +2,11 @@ #define C4FOWLIGHTSECTION_H #include "C4Rect.h" -#include "C4FoWLight.h" +class C4FoWLight; class C4FoWRegion; class C4FoWBeam; +class C4FoWBeamTriangle; /** The light section manages the beams for one light for one direction of 90°. @@ -16,7 +17,7 @@ class C4FoWBeam; class C4FoWLightSection { public: - C4FoWLightSection(C4FoWLight *pLight, int r, C4FoWLightSection *pNext = NULL); + C4FoWLightSection(C4FoWLight *pLight, int r); ~C4FoWLightSection(); private: @@ -30,20 +31,17 @@ private: int ra, rb, rc, rd; /* This section's beams */ - class C4FoWBeam *pBeams; - - C4FoWLightSection *pNext; - + C4FoWBeam *pBeams; + public: - - C4FoWLightSection *getNext() const { return pNext; } - + /** Recalculate of all light beams within the given rectangle because the landscape changed. */ void Invalidate(C4Rect r); /** Update all light beams within the given rectangle */ void Update(C4Rect r); - void Render(C4FoWRegion *pRegion, const class C4TargetFacet *pOnScreen = NULL); + + std::list CalculateTriangles(C4FoWRegion *pRegion); /** Shorten all light beams to the given reach. Called when the size of the light has decreased to the given value */ @@ -92,23 +90,15 @@ private: inline int32_t RectRightMostX(const C4Rect &r) const { return r.x + r.Wdt; } inline int32_t RectRightMostY(const C4Rect &r) const { return Max(0, r.x + r.Wdt <= 0 ? r.y + r.Hgt : r.y); } - inline void LightBallExtremePoint(float x, float y, float dir, float &lightX, float &lightY) const - { - float d = sqrt(x * x + y * y); - float s = Min(float(pLight->getSize()), d / 5.0f); - lightX = dir * y * s / d; - lightY = dir * -x * s / d; - } + inline void LightBallExtremePoint(float x, float y, float dir, float &lightX, float &lightY) const; /** Outputs the rightmost position of the light ball, as seen from the given point. Shrinks the light if it is too close to work against excessive fades. The light ball is the imaginery size of the light to enable soft shadows. */ - inline void LightBallRightMostPoint(float x, float y, float &lightX, float &lightY) const - { LightBallExtremePoint(x,y,+1.0f,lightX,lightY); } + inline void LightBallRightMostPoint(float x, float y, float &lightX, float &lightY) const; /** Outputs the leftmost position of the light ball, as seen from the given point. Shrinks the light if it is too close to work against excessive fades. The light ball is the imaginery size of the light to enable soft shadows. */ - inline void LightBallLeftMostPoint(float x, float y, float &lightX, float &lightY) const - { LightBallExtremePoint(x,y,-1.0f,lightX,lightY); } + inline void LightBallLeftMostPoint(float x, float y, float &lightX, float &lightY) const; /** Find right-most beam left of point */