forked from Mirrors/openclonk
refactor and document: C4FoWLightSection only calculates the triangles now, C4FoWLight stitches everything together and renders
* intermediate fade triangles are now calculated in C4FoWLight * rendering takes also place in C4FoWLight, using different C4FoWDrawStrategies * solved an old TODO from Peter (int -> int32_t) * refactor and simplify portions of the light vertex calculation codeissue1247
parent
00447dd524
commit
fd0163ebc6
|
@ -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
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
|
||||
#include <float.h>
|
||||
|
||||
// TODO: Make sure to use int32_t throughout!
|
||||
|
||||
//#define LIGHT_DEBUG
|
||||
|
||||
C4FoW::C4FoW()
|
||||
: pLights(NULL)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef C4FOWDRAWSTRATEGY_H
|
||||
#define C4FOWDRAWSTRATEGY_H
|
||||
|
||||
#include "C4DrawGL.h"
|
||||
#include <list>
|
||||
|
||||
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
|
|
@ -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<C4FoWBeamTriangle> 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<class C4FoWBeamTriangle> &triangles)
|
||||
{
|
||||
for (std::list<C4FoWBeamTriangle>::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<C4FoWBeamTriangle> &triangles)
|
||||
{
|
||||
pen->BeginFan();
|
||||
pen->DrawLightVertex(getX(), getY());
|
||||
|
||||
for (std::list<C4FoWBeamTriangle>::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<C4FoWBeamTriangle> &triangles)
|
||||
{
|
||||
pen->BeginFade();
|
||||
|
||||
for (std::list<C4FoWBeamTriangle>::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<C4FoWBeamTriangle> &triangles)
|
||||
{
|
||||
pen->BeginIntermediateFade();
|
||||
|
||||
for (std::list<C4FoWBeamTriangle>::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();
|
||||
}
|
||||
|
|
|
@ -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<class C4FoWBeamTriangle> &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<class C4FoWBeamTriangle> &triangles);
|
||||
/** Draws the fadeoot triangles - those around the triangle fan - with the given strategy */
|
||||
void DrawFade(C4FoWDrawStrategy* pen, std::list<C4FoWBeamTriangle> &triangles);
|
||||
/** Draws the fadeout triangles in between the normal fadeout triangles with the given strategy */
|
||||
void DrawIntermediateFadeTriangles(C4FoWDrawStrategy* pen, std::list<C4FoWBeamTriangle> &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
|
|
@ -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<C4FoWBeamTriangle> 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<C4FoWBeamTriangle> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<C4FoWBeamTriangle> 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 */
|
||||
|
|
Loading…
Reference in New Issue