forked from Mirrors/openclonk
split up C4FoW header and source file into one file for each class
* added some class and method documentation, removed some superfluous comments like void C4FoW::Update(C4Rect r) { // Update all lights ... * added ASK comments that need clarification before proper documentationissue1247
parent
c2f57db54b
commit
21e532da23
|
@ -286,8 +286,16 @@ set(OC_CLONK_SOURCES
|
|||
src/gui/C4StartupScenSelDlg.h
|
||||
src/gui/C4UpperBoard.cpp
|
||||
src/gui/C4UpperBoard.h
|
||||
src/landscape/C4FoW.cpp
|
||||
src/landscape/C4FoW.h
|
||||
src/landscape/fow/C4FoW.cpp
|
||||
src/landscape/fow/C4FoW.h
|
||||
src/landscape/fow/C4FoWRay.cpp
|
||||
src/landscape/fow/C4FoWRay.h
|
||||
src/landscape/fow/C4FoWLight.cpp
|
||||
src/landscape/fow/C4FoWLight.h
|
||||
src/landscape/fow/C4FoWLightSection.cpp
|
||||
src/landscape/fow/C4FoWLightSection.h
|
||||
src/landscape/fow/C4FoWRegion.cpp
|
||||
src/landscape/fow/C4FoWRegion.h
|
||||
src/landscape/C4Landscape.cpp
|
||||
src/landscape/C4Landscape.h
|
||||
src/landscape/C4LandscapeRenderClassic.cpp
|
||||
|
@ -718,6 +726,7 @@ include_directories(
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/src/graphics
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/gui
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/landscape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/landscape/fow
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/lib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/network
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/object
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
#include <C4PlayerList.h>
|
||||
#include <C4GameObjects.h>
|
||||
#include <C4Network2.h>
|
||||
#include <C4FoW.h>
|
||||
#include <C4FoWRegion.h>
|
||||
|
||||
void C4Viewport::DropFile(const char* fileName, float x, float y)
|
||||
{
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
|
||||
#ifndef C4FOW_H
|
||||
#define C4FOW_H
|
||||
|
||||
#include "C4Rect.h"
|
||||
#include "C4Surface.h"
|
||||
#include "C4DrawGL.h"
|
||||
|
||||
class C4FoW
|
||||
{
|
||||
public:
|
||||
C4FoW();
|
||||
|
||||
private:
|
||||
class C4FoWLight *pLights;
|
||||
|
||||
public:
|
||||
void Clear();
|
||||
|
||||
void Add(C4Object *pObj);
|
||||
void Remove(C4Object *pObj);
|
||||
void Update(C4Rect r);
|
||||
void Invalidate(C4Rect r);
|
||||
|
||||
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);
|
||||
};
|
||||
|
||||
class C4FoWRegion
|
||||
{
|
||||
public:
|
||||
C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer);
|
||||
~C4FoWRegion();
|
||||
|
||||
private:
|
||||
C4FoW *pFoW;
|
||||
C4Player *pPlayer;
|
||||
C4Rect Region, OldRegion;
|
||||
C4Surface *pSurface, *pBackSurface;
|
||||
GLuint hFrameBufDraw, hFrameBufRead;
|
||||
|
||||
public:
|
||||
const C4Rect &getRegion() const { return Region; }
|
||||
const C4Surface *getSurface() const { return pSurface; }
|
||||
const C4Surface *getBackSurface() const { return pBackSurface; }
|
||||
|
||||
void Clear();
|
||||
|
||||
void Update(C4Rect r);
|
||||
void Render(const C4TargetFacet *pOnScreen = NULL);
|
||||
|
||||
private:
|
||||
bool BindFramebuf();
|
||||
|
||||
};
|
||||
|
||||
class C4FoWLight
|
||||
{
|
||||
friend class C4FoW;
|
||||
public:
|
||||
C4FoWLight(C4Object *pObj);
|
||||
~C4FoWLight();
|
||||
|
||||
private:
|
||||
int32_t iX, iY; // center position
|
||||
int32_t iReach; // maximum length of rays
|
||||
int32_t iFadeout; // number of pixels over which rays fade out
|
||||
int32_t iSize; // size of the light source. Decides smoothness of shadows
|
||||
class C4FoWLightSection *pSections;
|
||||
C4FoWLight *pNext;
|
||||
C4Object *pObj; // Associated object
|
||||
|
||||
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; }
|
||||
int32_t getTotalReach() const { return iReach + iFadeout; }
|
||||
int32_t getSize() const { return iSize; }
|
||||
C4FoWLight *getNext() const { return pNext; }
|
||||
C4Object *getObj() const { return pObj; }
|
||||
|
||||
void SetReach(int32_t iReach, int32_t iFadeout);
|
||||
|
||||
void Invalidate(C4Rect r);
|
||||
void Update(C4Rect r);
|
||||
|
||||
void Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);
|
||||
|
||||
};
|
||||
|
||||
class C4FoWLightSection
|
||||
{
|
||||
public:
|
||||
C4FoWLightSection(C4FoWLight *pLight, int r, C4FoWLightSection *pNext = NULL);
|
||||
~C4FoWLightSection();
|
||||
|
||||
private:
|
||||
|
||||
// Center light
|
||||
C4FoWLight *pLight;
|
||||
|
||||
// Transformation matrix
|
||||
int iRot;
|
||||
int a, b, c, d;
|
||||
int ra, rb, rc, rd;
|
||||
|
||||
// Rays
|
||||
class C4FoWRay *pRays;
|
||||
|
||||
// List
|
||||
C4FoWLightSection *pNext;
|
||||
|
||||
public:
|
||||
|
||||
C4FoWLightSection *getNext() const { return pNext; }
|
||||
|
||||
void Invalidate(C4Rect r);
|
||||
void Update(C4Rect r);
|
||||
|
||||
void Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);
|
||||
|
||||
void Prune(int32_t iReach);
|
||||
void Dirty(int32_t iReach);
|
||||
|
||||
void ClearRays();
|
||||
|
||||
private:
|
||||
|
||||
// Ray to landscape. Ray coordinates are with light source at (0,0).
|
||||
template <class T> T transDX(T dx, T dy) const { return T(a) * dx + T(b) * dy; }
|
||||
template <class T> T transDY(T dx, T dy) const { return T(c) * dx + T(d) * dy; }
|
||||
template <class T> T transX(T x, T y) const { return transDX(x, y) + T(pLight->getX()); }
|
||||
template <class T> T transY(T x, T y) const { return transDY(x, y) + T(pLight->getY()); }
|
||||
|
||||
// Landscape to ray
|
||||
template <class T> T rtransDX(T dx, T dy) const { return T(ra) * dx + T(rb) * dy; }
|
||||
template <class T> T rtransDY(T dx, T dy) const { return T(rc) * dx + T(rd) * dy; }
|
||||
template <class T> T rtransX(T x, T y) const { return rtransDX(x-T(pLight->getX()),y-T(pLight->getY())); }
|
||||
template <class T> T rtransY(T x, T y) const { return rtransDY(x-T(pLight->getX()),y-T(pLight->getY())); }
|
||||
|
||||
C4Rect rtransRect(C4Rect r) const {
|
||||
C4Rect Rect(rtransX(r.x, r.y), rtransY(r.x, r.y),
|
||||
rtransDX(r.Wdt, r.Hgt), rtransDY(r.Wdt, r.Hgt));
|
||||
Rect.Normalize();
|
||||
return Rect;
|
||||
}
|
||||
|
||||
bool isConsistent() const;
|
||||
|
||||
int32_t RectLeftMostY(const C4Rect &r) const { return r.x >= 0 ? r.y+r.Hgt : r.y; }
|
||||
int32_t RectRightMostY(const C4Rect &r) const { return r.x + r.Wdt <= 0 ? r.y+r.Hgt : r.y; }
|
||||
|
||||
C4FoWRay *FindRayLeftOf(int32_t x, int32_t y); // find right-most ray left of point
|
||||
C4FoWRay *FindRayOver(int32_t x, int32_t y); // find left-most ray to extend over point
|
||||
|
||||
};
|
||||
|
||||
class C4FoWRay
|
||||
{
|
||||
public:
|
||||
C4FoWRay(int32_t iLeftX, int32_t iLeftY, int32_t iRightX, int32_t iRightY)
|
||||
: iLeftX(iLeftX), iLeftY(iLeftY), iRightX(iRightX), iRightY(iRightY),
|
||||
iLeftEndY(0), iRightEndY(0),
|
||||
iError(0),
|
||||
fDirty(true),
|
||||
pNext(NULL)
|
||||
{ }
|
||||
|
||||
private:
|
||||
int32_t iLeftX, iLeftY; // left delimiter point
|
||||
int32_t iRightX, iRightY; // right delimiter point
|
||||
int32_t iLeftEndY, iRightEndY; // where it hit solid material. C4FoWRayActive while currently being followed.
|
||||
int32_t iError; // How much error this ray has
|
||||
bool fDirty; // landscape changed since it was followed?
|
||||
C4FoWRay *pNext;
|
||||
|
||||
public:
|
||||
bool isDirty() const { return fDirty; }
|
||||
bool isClean() const { return !fDirty; }
|
||||
C4FoWRay *getNext() const { return pNext; }
|
||||
|
||||
// Get a point on the ray boundary.
|
||||
inline int32_t getLeftX(int32_t y) const { return iLeftX * y / iLeftY; }
|
||||
inline int32_t getRightX(int32_t y) const { return iRightX * y / iRightY; }
|
||||
inline float getLeftXf(int32_t y) const { return float(iLeftX * y) / float(iLeftY); }
|
||||
inline float getRightXf(int32_t y) const { return float(iRightX * y) / float(iRightY); }
|
||||
|
||||
int32_t getLeftEndY() const { return iLeftEndY; }
|
||||
int32_t getLeftEndX() const { return getLeftX(iLeftEndY); }
|
||||
float getLeftEndXf() const { return getLeftXf(iLeftEndY); }
|
||||
int32_t getRightEndY() const { return iRightEndY; }
|
||||
int32_t getRightEndX() const { return getRightX(iRightEndY); }
|
||||
float getRightEndXf() const { return getRightXf(iRightEndY); }
|
||||
|
||||
StdStrBuf getDesc() const;
|
||||
|
||||
bool isLeft(int x, int y) const {
|
||||
return iLeftX * y > x * iLeftY;
|
||||
}
|
||||
bool isRight(int x, int y) const {
|
||||
return iRightX * y < x * iRightY;
|
||||
}
|
||||
bool isInside(int x, int y) const {
|
||||
return !isLeft(x, y) && !isRight(x, y);
|
||||
}
|
||||
|
||||
void SetLeft(int x, int y) { iLeftX = x; iLeftY = y; }
|
||||
void SetRight(int x, int y) { iRightX = x; iRightY = y; }
|
||||
|
||||
bool MergeRight(int x, int y);
|
||||
bool MergeLeft(int x, int y);
|
||||
bool Eliminate(int x, int y);
|
||||
C4FoWRay *Split(int x, int y);
|
||||
void MergeDirty();
|
||||
|
||||
void Clean(int32_t y);
|
||||
void Dirty(int32_t y);
|
||||
void Prune(int32_t y);
|
||||
|
||||
};
|
||||
|
||||
#endif // C4FOW_H
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "C4Landscape.h"
|
||||
#include "C4Texture.h"
|
||||
#include "C4FoW.h"
|
||||
#include "C4FoWRegion.h"
|
||||
|
||||
#include "C4GroupSet.h"
|
||||
#include "C4Components.h"
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
|
||||
#include "C4Include.h"
|
||||
#include "C4FoW.h"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
// TODO: Make sure to use int32_t throughout!
|
||||
|
||||
//#define LIGHT_DEBUG
|
||||
|
||||
C4FoW::C4FoW()
|
||||
: pLights(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void C4FoW::Add(C4Object *pObj)
|
||||
{
|
||||
// No view range? Probably want to remove instead
|
||||
if(!pObj->PlrViewRange)
|
||||
{
|
||||
Remove(pObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for matching light
|
||||
C4FoWLight *pLight;
|
||||
for (pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
if (pLight->getObj() == pObj)
|
||||
break;
|
||||
|
||||
if (pLight)
|
||||
{
|
||||
|
||||
// Update reach
|
||||
pLight->SetReach(pObj->PlrViewRange, 50);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new light otherwise
|
||||
pLight = new C4FoWLight(pObj);
|
||||
pLight->pNext = pLights;
|
||||
pLights = pLight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void C4FoW::Remove(C4Object *pObj)
|
||||
{
|
||||
// Look for matching light
|
||||
C4FoWLight *pPrev = NULL, *pLight;
|
||||
for (pLight = pLights; pLight; pPrev = pLight, pLight = pLight->getNext())
|
||||
if (pLight->getObj() == pObj)
|
||||
break;
|
||||
if (!pLight)
|
||||
return;
|
||||
|
||||
// Remove
|
||||
(pPrev ? pLights : pPrev->pNext) = pLight->getNext();
|
||||
delete pLight;
|
||||
}
|
||||
|
||||
void C4FoW::Invalidate(C4Rect r)
|
||||
{
|
||||
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
pLight->Invalidate(r);
|
||||
}
|
||||
|
||||
void C4FoW::Update(C4Rect r)
|
||||
{
|
||||
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
pLight->Update(r);
|
||||
}
|
||||
|
||||
void C4FoW::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen)
|
||||
{
|
||||
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
pLight->Render(pRegion, pOnScreen);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
#ifndef C4FOW_H
|
||||
#define C4FOW_H
|
||||
|
||||
#include "C4Surface.h"
|
||||
#include "C4FacetEx.h"
|
||||
#include "C4Rect.h"
|
||||
#include "C4Object.h"
|
||||
#include "C4FoWLight.h"
|
||||
|
||||
/**
|
||||
This class holds all lights for the objects. It forwards the update, invalidation and render calls each to the
|
||||
lights.
|
||||
*/
|
||||
class C4FoW
|
||||
{
|
||||
public:
|
||||
C4FoW();
|
||||
|
||||
private:
|
||||
/** linked list of all lights */
|
||||
class C4FoWLight *pLights;
|
||||
|
||||
public:
|
||||
void Clear();
|
||||
|
||||
/** Updates the view range of the given object in its associated light or create a new light if none exists yet. */
|
||||
void Add(C4Object *pObj);
|
||||
/** Removes the light associated with the given object, if any */
|
||||
void Remove(C4Object *pObj);
|
||||
|
||||
/** Update all light rays within the given rectangle */
|
||||
// ASK: only called by C4FoWRegion??
|
||||
void Update(C4Rect r);
|
||||
/** Triggers the recalculation of all light rays within the given rectangle because the landscape changed. */
|
||||
void Invalidate(C4Rect r);
|
||||
|
||||
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);
|
||||
};
|
||||
|
||||
#endif // C4FOW_H
|
|
@ -0,0 +1,77 @@
|
|||
|
||||
#include "C4Include.h"
|
||||
#include "C4FoWLight.h"
|
||||
#include "C4FoWLightSection.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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
void C4FoWLight::SetReach(int32_t iReach2, int32_t iFadeout2)
|
||||
{
|
||||
// Fadeout changes don't matter
|
||||
iFadeout = iFadeout2;
|
||||
|
||||
if (iReach == iReach2) return;
|
||||
|
||||
// Reach decreased? Prune rays
|
||||
if (iReach2 < iReach) {
|
||||
iReach = iReach2;
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Prune(iReach);
|
||||
|
||||
} else {
|
||||
|
||||
// Reach increased? Dirty rays that might get longer now
|
||||
iReach = iReach2;
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Dirty(iReach);
|
||||
}
|
||||
}
|
||||
|
||||
void C4FoWLight::Update(C4Rect Rec)
|
||||
{
|
||||
|
||||
// Update position from object. Clear if we moved in any way
|
||||
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 rays being cleared and new ones created
|
||||
pSect->Prune(0);
|
||||
iX = iNX; iY = iNY;
|
||||
}
|
||||
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Update(Rec);
|
||||
|
||||
}
|
||||
|
||||
void C4FoWLight::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen)
|
||||
{
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Render(pRegion, pOnScreen);
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef C4FOWLIGHT_H
|
||||
#define C4FOWLIGHT_H
|
||||
|
||||
#include "C4Object.h"
|
||||
#include "C4Rect.h"
|
||||
#include "C4Surface.h"
|
||||
#include "C4FacetEx.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 rays for each direction (up, down, left, right).
|
||||
|
||||
Furthermore, each light source has a size. This is usually the object's PlrViewRange. See SetReach.
|
||||
*/
|
||||
class C4FoWLight
|
||||
{
|
||||
friend class C4FoW;
|
||||
public:
|
||||
C4FoWLight(C4Object *pObj);
|
||||
~C4FoWLight();
|
||||
|
||||
private:
|
||||
int32_t iX, iY; // center position
|
||||
int32_t iReach; // maximum length of rays
|
||||
int32_t iFadeout; // number of pixels over which rays fade out
|
||||
int32_t iSize; // size of the light source. Decides smoothness of shadows
|
||||
class C4FoWLightSection *pSections;
|
||||
C4FoWLight *pNext;
|
||||
C4Object *pObj; // Associated object
|
||||
|
||||
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
|
||||
int32_t getTotalReach() const { return iReach + iFadeout; }
|
||||
int32_t getSize() const { return iSize; }
|
||||
C4FoWLight *getNext() const { return pNext; }
|
||||
C4Object *getObj() const { return pObj; }
|
||||
|
||||
/** Sets the light's size in pixels. The reach is the total radius of the light while the fadeout is the number of
|
||||
pixels after which the light should dim down */
|
||||
void SetReach(int32_t iReach, int32_t iFadeout);
|
||||
|
||||
/** Triggers the recalculation of all light rays within the given rectangle for this light because the landscape changed. */
|
||||
void Invalidate(C4Rect r);
|
||||
/** Update all light rays within the given rectangle for this light */
|
||||
void Update(C4Rect r);
|
||||
|
||||
void Render(class C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,121 +1,49 @@
|
|||
|
||||
#include "C4Include.h"
|
||||
#include "C4FoW.h"
|
||||
#include "C4Rect.h"
|
||||
#include "C4FoWLightSection.h"
|
||||
#include "C4FoWRay.h"
|
||||
#include "C4FoWLight.h"
|
||||
#include "C4FoWRegion.h"
|
||||
#include "C4Landscape.h"
|
||||
#include "C4DrawGL.h"
|
||||
#include "C4Object.h"
|
||||
|
||||
#include <float.h>
|
||||
// Gives the point where the line through (x1,y1) and (x2,y2) crosses through the line
|
||||
// through (x3,y3) and (x4,y4)
|
||||
bool find_cross(float x1, float y1, float x2, float y2,
|
||||
float x3, float y3, float x4, float y4,
|
||||
float *px, float *py, float *pb = NULL)
|
||||
{
|
||||
// We are looking for a, b so that
|
||||
// px = a*x1 + (1-a)*x2 = b*x3 + (1-b)*x4
|
||||
// py = a*y1 + (1-a)*y2 = b*y3 + (1-b)*y4
|
||||
|
||||
// TODO: Make sure to use int32_t throughout!
|
||||
// Cross product
|
||||
float d = (x3-x4)*(y1-y2) - (y3-y4)*(x1-x2);
|
||||
if (d == 0) return false; // parallel - or vector(s) 0
|
||||
|
||||
//#define LIGHT_DEBUG
|
||||
// We actually just need b - the unique solution
|
||||
// to above equation. A refreshing piece of elementary math
|
||||
// that I got wrong two times.
|
||||
float b = ((y4-y2)*(x1-x2) - (x4-x2)*(y1-y2)) / d;
|
||||
*px = b*x3 + (1-b)*x4;
|
||||
*py = b*y3 + (1-b)*y4;
|
||||
if (pb) *pb = b;
|
||||
|
||||
bool glCheck() {
|
||||
if (int err = glGetError()) {
|
||||
LogF("GL error %d: %s", err, gluErrorString(err));
|
||||
return false;
|
||||
// Sanity-test solution
|
||||
#ifdef _DEBUG
|
||||
if (x1 != x2) {
|
||||
float a = (b*(x3-x4) + (x4-x2)) / (x1-x2);
|
||||
float eps = 0.01f;
|
||||
//assert(fabs(a*x1 + (1-a)*x2 - *px) < eps);
|
||||
//assert(fabs(a*y1 + (1-a)*y2 - *py) < eps);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const float C4FoWSmooth = 8.0;
|
||||
|
||||
// Maximum error allowed while merging rays. Actually double, see below.
|
||||
const int32_t C4FoWMergeThreshold = 10; // (in landscape pixels)
|
||||
|
||||
// A = 1/2 | a x b |
|
||||
static inline int32_t getTriangleSurface(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3)
|
||||
{
|
||||
int32_t ax = x2 - x1, ay = y2 - y1;
|
||||
int32_t bx = x3 - x1, by = y3 - y1;
|
||||
// We don't bother to actually halve so we can stay with integers.
|
||||
// Doesn't matter as long as we keep in mind the threshold needs to
|
||||
// be doubled.
|
||||
return abs(ax * by - ay * bx);
|
||||
}
|
||||
|
||||
C4FoW::C4FoW()
|
||||
: pLights(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
void C4FoW::Add(C4Object *pObj)
|
||||
{
|
||||
// No view range? Probably want to remove instead
|
||||
if(!pObj->PlrViewRange) {
|
||||
Remove(pObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for matching light
|
||||
C4FoWLight *pLight;
|
||||
for (pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
if (pLight->getObj() == pObj)
|
||||
break;
|
||||
if (pLight) {
|
||||
|
||||
// Update reach
|
||||
pLight->SetReach(pObj->PlrViewRange, 50);
|
||||
|
||||
} else {
|
||||
|
||||
// Create new light otherwise
|
||||
pLight = new C4FoWLight(pObj);
|
||||
pLight->pNext = pLights;
|
||||
pLights = pLight;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void C4FoW::Remove(C4Object *pObj)
|
||||
{
|
||||
|
||||
// Look for matching light
|
||||
C4FoWLight *pPrev = NULL, *pLight;
|
||||
for (pLight = pLights; pLight; pPrev = pLight, pLight = pLight->getNext())
|
||||
if (pLight->getObj() == pObj)
|
||||
break;
|
||||
if (!pLight)
|
||||
return;
|
||||
|
||||
// Remove
|
||||
(pPrev ? pLights : pPrev->pNext) = pLight->getNext();
|
||||
delete pLight;
|
||||
}
|
||||
|
||||
void C4FoW::Invalidate(C4Rect r)
|
||||
{
|
||||
// Invalidate all lights
|
||||
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
pLight->Invalidate(r);
|
||||
}
|
||||
|
||||
void C4FoWLight::Invalidate(C4Rect r)
|
||||
{
|
||||
// Invalidate all sections
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Invalidate(r);
|
||||
}
|
||||
|
||||
void C4FoW::Update(C4Rect r)
|
||||
{
|
||||
// Update all lights
|
||||
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
pLight->Update(r);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
pSections = new C4FoWLightSection(this, 0);
|
||||
pSections = new C4FoWLightSection(this, 90, pSections);
|
||||
pSections = new C4FoWLightSection(this, 180, pSections);
|
||||
pSections = new C4FoWLightSection(this, 270, pSections);
|
||||
}
|
||||
|
||||
C4FoWLightSection::C4FoWLightSection(C4FoWLight *pLight, int r, C4FoWLightSection *pNext)
|
||||
: pLight(pLight), iRot(r), pNext(pNext)
|
||||
|
@ -146,14 +74,6 @@ C4FoWLightSection::C4FoWLightSection(C4FoWLight *pLight, int r, C4FoWLightSectio
|
|||
pRays = new C4FoWRay(-1, +1, +1, +1);
|
||||
}
|
||||
|
||||
C4FoWLight::~C4FoWLight()
|
||||
{
|
||||
while (C4FoWLightSection *pSect = pSections) {
|
||||
pSections = pSect->getNext();
|
||||
delete pSect;
|
||||
}
|
||||
}
|
||||
|
||||
C4FoWLightSection::~C4FoWLightSection()
|
||||
{
|
||||
ClearRays();
|
||||
|
@ -167,29 +87,6 @@ void C4FoWLightSection::ClearRays()
|
|||
}
|
||||
}
|
||||
|
||||
void C4FoWLight::SetReach(int32_t iReach2, int32_t iFadeout2)
|
||||
{
|
||||
// Fadeout changes don't matter
|
||||
iFadeout = iFadeout2;
|
||||
|
||||
// Reach unchanged? Easy.
|
||||
if (iReach == iReach2) return;
|
||||
|
||||
// Reach decreased? Prune rays
|
||||
if (iReach2 < iReach) {
|
||||
iReach = iReach2;
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Prune(iReach);
|
||||
|
||||
} else {
|
||||
|
||||
// Reach increased? Dirty rays that might get longer now
|
||||
iReach = iReach2;
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Dirty(iReach);
|
||||
}
|
||||
}
|
||||
|
||||
void C4FoWLightSection::Prune(int32_t iReach)
|
||||
{
|
||||
if (iReach == 0) {
|
||||
|
@ -230,23 +127,6 @@ C4FoWRay *C4FoWLightSection::FindRayOver(int32_t x, int32_t y)
|
|||
return pPrev ? pPrev->getNext() : pRays;
|
||||
}
|
||||
|
||||
void C4FoWLight::Update(C4Rect Rec)
|
||||
{
|
||||
|
||||
// Update position from object. Clear if we moved in any way
|
||||
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())
|
||||
pSect->Prune(0);
|
||||
iX = iNX; iY = iNY;
|
||||
}
|
||||
|
||||
// Update all sections
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Update(Rec);
|
||||
|
||||
}
|
||||
|
||||
void C4FoWLightSection::Update(C4Rect RectIn)
|
||||
{
|
||||
|
||||
|
@ -441,6 +321,14 @@ void C4FoWLightSection::Update(C4Rect RectIn)
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool C4FoWLightSection::isConsistent() const {
|
||||
return (a * c + b * d == 1) && (ra * rc + rb * rd == 1) &&
|
||||
(a * ra + b * rc == 1) && (a * rb + b * rd == 0) &&
|
||||
(c * ra + d * rc == 0) && (c * rb + d * rd == 1);
|
||||
}
|
||||
|
||||
void C4FoWLightSection::Invalidate(C4Rect r)
|
||||
{
|
||||
// Assume normalized rectangle
|
||||
|
@ -476,390 +364,6 @@ void C4FoWLightSection::Invalidate(C4Rect r)
|
|||
|
||||
}
|
||||
|
||||
bool C4FoWRay::MergeRight(int x, int y)
|
||||
{
|
||||
// Note: Right-merging is the most common and most important optimization.
|
||||
// This procedure will probably be *hammered* as a result. Worth inlining?
|
||||
|
||||
assert(!isDirty()); assert(isRight(x, y));
|
||||
|
||||
// Calculate error. Note that simply summing up errors is not correct,
|
||||
// strictly speaking (as new and old error surfaces might overlap). Still,
|
||||
// this is quite elaborate already, no need to make it even more
|
||||
int32_t iErr = getTriangleSurface(
|
||||
getLeftEndX(), iLeftEndY,
|
||||
getRightEndX(), iRightEndY,
|
||||
x, y);
|
||||
if (iError + iErr > C4FoWMergeThreshold)
|
||||
return false;
|
||||
|
||||
// Move right endpoint.
|
||||
iRightX = x;
|
||||
iRightY = y;
|
||||
iRightEndY = y;
|
||||
iError += iErr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C4FoWRay::MergeLeft(int x, int y)
|
||||
{
|
||||
assert(!isDirty()); assert(isLeft(x, y));
|
||||
|
||||
// Calculate error.
|
||||
float iErr = getTriangleSurface(
|
||||
getLeftEndX(), iLeftEndY,
|
||||
getRightEndX(), iRightEndY,
|
||||
x, y);
|
||||
if (iError + iErr > C4FoWMergeThreshold)
|
||||
return false;
|
||||
|
||||
// Move left endpoint.
|
||||
iLeftX = x;
|
||||
iLeftY = y;
|
||||
iLeftEndY = y;
|
||||
iError += iErr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C4FoWRay::Eliminate(int x, int y)
|
||||
{
|
||||
// Called on the ray left of the one getting eliminated
|
||||
C4FoWRay *pElim = pNext, *pMerge = pNext->pNext;
|
||||
assert(!!pElim); assert(!!pMerge);
|
||||
assert(!isDirty()); assert(!pMerge->isDirty());
|
||||
|
||||
// Calc errors, add those accumulated on both merged rays
|
||||
int32_t iErr = getTriangleSurface(
|
||||
getLeftEndX(), iLeftEndY,
|
||||
pMerge->getRightEndX(), pMerge->iLeftEndY,
|
||||
x, y);
|
||||
iErr += iError + pMerge->iError;
|
||||
if (iErr > C4FoWMergeThreshold)
|
||||
return false;
|
||||
|
||||
// Do elimination
|
||||
iRightX = pMerge->iRightX;
|
||||
iRightY = pMerge->iRightY;
|
||||
iRightEndY = pMerge->iRightEndY;
|
||||
pNext = pMerge->pNext;
|
||||
delete pElim;
|
||||
delete pMerge;
|
||||
return true;
|
||||
}
|
||||
|
||||
C4FoWRay *C4FoWRay::Split(int x, int y)
|
||||
{
|
||||
// Make sure to newer create negative-surface rays
|
||||
assert(isDirty()); assert(isInside(x, y));
|
||||
|
||||
// Allocate a new ray. Ugh, expensive.
|
||||
C4FoWRay *pRay = new C4FoWRay(x, y, iRightX, iRightY);
|
||||
pRay->Dirty(iLeftEndY);
|
||||
|
||||
// Move to make space
|
||||
iRightX = x;
|
||||
iRightY = y;
|
||||
|
||||
// Relink
|
||||
pRay->pNext = pNext;
|
||||
pNext = pRay;
|
||||
return pRay;
|
||||
}
|
||||
|
||||
void C4FoWRay::MergeDirty()
|
||||
{
|
||||
// As a rule, dirty rays following each other should
|
||||
// always be merged, so splits can be reverted once
|
||||
// the landscape changes.
|
||||
C4FoWRay *pWith = pNext;
|
||||
assert(isDirty()); assert(!!pWith); assert(pWith->isDirty());
|
||||
|
||||
// Figure out how far the new dirty ray reaches. Note that
|
||||
// we might lose information about the landscape here.
|
||||
Dirty(Min(getLeftEndY(), pWith->getLeftEndY()));
|
||||
|
||||
// Set right
|
||||
iRightX = pWith->iRightX;
|
||||
iRightY = pWith->iRightY;
|
||||
|
||||
// Relink & delete
|
||||
pNext = pWith->getNext();
|
||||
delete pWith;
|
||||
}
|
||||
|
||||
void C4FoWRay::Clean(int y)
|
||||
{
|
||||
// Search hit something, this ray is now clean.
|
||||
assert(isDirty());
|
||||
iLeftEndY = y;
|
||||
iRightEndY = y;
|
||||
fDirty = false;
|
||||
}
|
||||
|
||||
void C4FoWRay::Dirty(int y)
|
||||
{
|
||||
// Got invalidated, ray is dirty until updated
|
||||
iLeftEndY = y;
|
||||
iRightEndY = y;
|
||||
fDirty = true;
|
||||
}
|
||||
|
||||
void C4FoWRay::Prune(int32_t y)
|
||||
{
|
||||
// Check which sides we need to prune
|
||||
bool fLeft = (iLeftEndY >= y),
|
||||
fRight = (iRightEndY >= y);
|
||||
// If both sides got pruned, we are clean
|
||||
// (can't possibly extend this ray further)
|
||||
if (fLeft && fRight)
|
||||
Clean(y);
|
||||
else if (fLeft)
|
||||
iLeftEndY = y;
|
||||
if (fRight)
|
||||
iRightEndY = y;
|
||||
}
|
||||
|
||||
C4FoWRegion::C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer)
|
||||
: pFoW(pFoW)
|
||||
, pPlayer(pPlayer)
|
||||
, hFrameBufDraw(0), hFrameBufRead(0)
|
||||
, Region(0,0,0,0), OldRegion(0,0,0,0)
|
||||
, pSurface(NULL), pBackSurface(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
C4FoWRegion::~C4FoWRegion()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
bool C4FoWRegion::BindFramebuf()
|
||||
{
|
||||
|
||||
// Flip texture
|
||||
C4Surface *pSfc = pSurface;
|
||||
pSurface = pBackSurface;
|
||||
pBackSurface = pSfc;
|
||||
|
||||
// Can simply reuse old texture?
|
||||
if (!pSurface || pSurface->Wdt < Region.Wdt || pSurface->Hgt < Region.Hgt)
|
||||
{
|
||||
// Create texture. Round up to next power of two in order to
|
||||
// prevent rounding errors, as well as preventing lots of
|
||||
// re-allocations when region size changes quickly (think zoom).
|
||||
if (!pSurface)
|
||||
pSurface = new C4Surface();
|
||||
int iWdt = 1, iHgt = 1;
|
||||
while (iWdt < Region.Wdt) iWdt *= 2;
|
||||
while (iHgt < Region.Hgt) iHgt *= 2;
|
||||
if (!pSurface->Create(iWdt, iHgt))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate frame buffer object
|
||||
if (!hFrameBufDraw) {
|
||||
glGenFramebuffersEXT(1, &hFrameBufDraw);
|
||||
glGenFramebuffersEXT(1, &hFrameBufRead);
|
||||
}
|
||||
|
||||
// Bind current texture to frame buffer
|
||||
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, hFrameBufDraw);
|
||||
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, hFrameBufRead);
|
||||
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT,
|
||||
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
|
||||
pSurface->ppTex[0]->texName, 0);
|
||||
if (pBackSurface)
|
||||
glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT,
|
||||
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
|
||||
pBackSurface->ppTex[0]->texName, 0);
|
||||
|
||||
// Check status, unbind if something was amiss
|
||||
GLenum status1 = glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT),
|
||||
status2 = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
|
||||
if (status1 != GL_FRAMEBUFFER_COMPLETE_EXT ||
|
||||
(pBackSurface && status2 != GL_FRAMEBUFFER_COMPLETE_EXT) ||
|
||||
!glCheck())
|
||||
{
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Worked!
|
||||
return true;
|
||||
}
|
||||
|
||||
void C4FoWRegion::Clear()
|
||||
{
|
||||
if (hFrameBufDraw) {
|
||||
glDeleteFramebuffersEXT(1, &hFrameBufDraw);
|
||||
glDeleteFramebuffersEXT(1, &hFrameBufRead);
|
||||
}
|
||||
hFrameBufDraw = hFrameBufRead = 0;
|
||||
delete pSurface; pSurface = NULL;
|
||||
delete pBackSurface; pBackSurface = NULL;
|
||||
}
|
||||
|
||||
void C4FoWRegion::Update(C4Rect r)
|
||||
{
|
||||
// Set the new region
|
||||
Region = r;
|
||||
}
|
||||
|
||||
void C4FoWRegion::Render(const C4TargetFacet *pOnScreen)
|
||||
{
|
||||
// Update FoW at interesting location
|
||||
pFoW->Update(Region);
|
||||
|
||||
// On screen? No need to set up frame buffer - simply shortcut
|
||||
if (pOnScreen)
|
||||
{
|
||||
pFoW->Render(this, pOnScreen);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create & bind the frame buffer
|
||||
pDraw->StorePrimaryClipper();
|
||||
if(!BindFramebuf())
|
||||
{
|
||||
pDraw->RestorePrimaryClipper();
|
||||
return;
|
||||
}
|
||||
assert(pSurface && hFrameBufDraw);
|
||||
if (!pSurface || !hFrameBufDraw)
|
||||
return;
|
||||
|
||||
// Set up a clean context
|
||||
glViewport(0, 0, getSurface()->Wdt, getSurface()->Hgt);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, getSurface()->Wdt, getSurface()->Hgt, 0);
|
||||
|
||||
// Clear texture contents
|
||||
glClearColor(0.0f, 0.5f/1.5f, 0.5f/1.5f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Copy over the old state
|
||||
if (OldRegion.Wdt > 0) {
|
||||
|
||||
int dx0 = Region.x - OldRegion.x,
|
||||
dy0 = Region.y - OldRegion.y,
|
||||
dx1 = Region.x + Region.Wdt - OldRegion.x - OldRegion.Wdt,
|
||||
dy1 = Region.y + Region.Hgt - OldRegion.y - OldRegion.Hgt;
|
||||
|
||||
/*glBlitFramebufferEXT(
|
||||
Max(0, dx0), Max(0, -dy1),
|
||||
OldRegion.Wdt - Max(0, -dx1), OldRegion.Hgt - Max(0, dy0),
|
||||
Max(0, -dx0), Max(0, dy1),
|
||||
Region.Wdt - Max(0, dx1), Region.Hgt - Max(0, -dy0),
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
*/
|
||||
|
||||
int sx0 = Max(0, dx0),
|
||||
sy0 = Max(0, dy1),
|
||||
sx1 = OldRegion.Wdt - Max(0, -dx1),
|
||||
sy1 = OldRegion.Hgt - Max(0, -dy0),
|
||||
tx0 = Max(0, -dx0),
|
||||
ty0 = Max(0, -dy1),
|
||||
tx1 = Region.Wdt - Max(0, dx1),
|
||||
ty1 = Region.Hgt - Max(0, dy0);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, getBackSurface()->ppTex[0]->texName);
|
||||
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glBegin(GL_QUADS);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glTexCoord2f(float(sx0)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy0)/getBackSurface()->Hgt); glVertex2i(tx0, ty0);
|
||||
glTexCoord2f(float(sx0)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy1)/getBackSurface()->Hgt); glVertex2i(tx0, ty1);
|
||||
glTexCoord2f(float(sx1)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy1)/getBackSurface()->Hgt); glVertex2i(tx1, ty1);
|
||||
glTexCoord2f(float(sx1)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy0)/getBackSurface()->Hgt); glVertex2i(tx1, ty0);
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glCheck();
|
||||
|
||||
// Fade out. Note we constantly vary the alpha factor all the time -
|
||||
// this is barely visible but makes it a lot less likely that we
|
||||
// hit cases where we add the same thing every time, but still don't
|
||||
// converge to the same color due to rounding.
|
||||
int iAdd = (Game.FrameCounter/3) % 2;
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.0f, 0.5f/1.5f, 0.5f/1.5f, 1.0f/16.0f+iAdd*1.0f/256.0f);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(0, 0);
|
||||
glVertex2i(getSurface()->Wdt, 0);
|
||||
glVertex2i(getSurface()->Wdt, getSurface()->Hgt);
|
||||
glVertex2i(0, getSurface()->Hgt);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// Render FoW to frame buffer object
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
pFoW->Render(this);
|
||||
|
||||
// Done!
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
pDraw->RestorePrimaryClipper();
|
||||
glCheck();
|
||||
|
||||
OldRegion = Region;
|
||||
|
||||
}
|
||||
|
||||
void C4FoW::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen)
|
||||
{
|
||||
// Render all lights
|
||||
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
|
||||
pLight->Render(pRegion, pOnScreen);
|
||||
}
|
||||
|
||||
void C4FoWLight::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen)
|
||||
{
|
||||
// Render all sections
|
||||
//C4FoWLightSection *pSect = pSections;
|
||||
for (C4FoWLightSection *pSect = pSections; pSect; pSect = pSect->getNext())
|
||||
pSect->Render(pRegion, pOnScreen);
|
||||
}
|
||||
|
||||
// Gives the point where the line through (x1,y1) and (x2,y2) crosses through the line
|
||||
// through (x3,y3) and (x4,y4)
|
||||
bool find_cross(float x1, float y1, float x2, float y2,
|
||||
float x3, float y3, float x4, float y4,
|
||||
float *px, float *py, float *pb = NULL)
|
||||
{
|
||||
// We are looking for a, b so that
|
||||
// px = a*x1 + (1-a)*x2 = b*x3 + (1-b)*x4
|
||||
// py = a*y1 + (1-a)*y2 = b*y3 + (1-b)*y4
|
||||
|
||||
// Cross product
|
||||
float d = (x3-x4)*(y1-y2) - (y3-y4)*(x1-x2);
|
||||
if (d == 0) return false; // parallel - or vector(s) 0
|
||||
|
||||
// We actually just need b - the unique solution
|
||||
// to above equation. A refreshing piece of elementary math
|
||||
// that I got wrong two times.
|
||||
float b = ((y4-y2)*(x1-x2) - (x4-x2)*(y1-y2)) / d;
|
||||
*px = b*x3 + (1-b)*x4;
|
||||
*py = b*y3 + (1-b)*y4;
|
||||
if (pb) *pb = b;
|
||||
|
||||
// Sanity-test solution
|
||||
#ifdef _DEBUG
|
||||
if (x1 != x2) {
|
||||
float a = (b*(x3-x4) + (x4-x2)) / (x1-x2);
|
||||
float eps = 0.01f;
|
||||
//assert(fabs(a*x1 + (1-a)*x2 - *px) < eps);
|
||||
//assert(fabs(a*y1 + (1-a)*y2 - *py) < eps);
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen)
|
||||
{
|
||||
C4Rect Reg = rtransRect(pRegion->getRegion());
|
||||
|
@ -1156,7 +660,7 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
|
|||
|
||||
// For once we actually calculate this using the real distance
|
||||
float dx = gFanLX[i] - gLightLX, dy = gFanLY[i] - gLightLY;
|
||||
float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy);;
|
||||
float d = float(pLight->getFadeout()) / sqrt(dx*dx + dy*dy);
|
||||
gFadeLX[i] = gFanLX[i] + d * dx;
|
||||
gFadeLY[i] = gFanLY[i] + d * dy;
|
||||
|
||||
|
@ -1478,18 +982,3 @@ void C4FoWLightSection::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScr
|
|||
|
||||
}
|
||||
|
||||
|
||||
StdStrBuf C4FoWRay::getDesc() const {
|
||||
return FormatString("%d:%d@%d:%d%s",
|
||||
getLeftX(1000),
|
||||
getRightX(1000),
|
||||
getLeftEndY(),
|
||||
getRightEndY(),
|
||||
fDirty ? "*" : "");
|
||||
}
|
||||
|
||||
bool C4FoWLightSection::isConsistent() const {
|
||||
return (a * c + b * d == 1) && (ra * rc + rb * rd == 1) &&
|
||||
(a * ra + b * rc == 1) && (a * rb + b * rd == 0) &&
|
||||
(c * ra + d * rc == 0) && (c * rb + d * rd == 1);
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
#ifndef C4FOWLIGHTSECTION_H
|
||||
#define C4FOWLIGHTSECTION_H
|
||||
|
||||
#include "C4Rect.h"
|
||||
#include "C4Surface.h"
|
||||
|
||||
class C4FoWLight;
|
||||
class C4FoWRegion;
|
||||
class C4FoWRay;
|
||||
|
||||
/** The light section manages the rays for one light for one direction of 90°. */
|
||||
class C4FoWLightSection
|
||||
{
|
||||
public:
|
||||
C4FoWLightSection(C4FoWLight *pLight, int r, C4FoWLightSection *pNext = NULL);
|
||||
~C4FoWLightSection();
|
||||
|
||||
private:
|
||||
|
||||
/* Center light */
|
||||
C4FoWLight *pLight;
|
||||
|
||||
/* Transformation matrix */
|
||||
int iRot;
|
||||
int a, b, c, d;
|
||||
int ra, rb, rc, rd;
|
||||
|
||||
/* This section's rays */
|
||||
class C4FoWRay *pRays;
|
||||
|
||||
C4FoWLightSection *pNext;
|
||||
|
||||
public:
|
||||
|
||||
C4FoWLightSection *getNext() const { return pNext; }
|
||||
|
||||
/** Recalculate of all light rays within the given rectangle because the landscape changed. */
|
||||
void Invalidate(C4Rect r);
|
||||
/** Update all light rays within the given rectangle */
|
||||
void Update(C4Rect r);
|
||||
|
||||
void Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen = NULL);
|
||||
|
||||
/** Shorten all light rays to the given reach.
|
||||
Called when the size of the light has decreased to the given value */
|
||||
void Prune(int32_t iReach);
|
||||
/** Extend all light rays to the given reach.
|
||||
Called when the size of the light has increased to the given value */
|
||||
void Dirty(int32_t iReach);
|
||||
|
||||
private:
|
||||
|
||||
/** Remove all rays. pRays is NULL after that. */
|
||||
void ClearRays();
|
||||
|
||||
// Ray coordinate to landscape coordinate. Ray coordinates are relative to the light source.
|
||||
template <class T> T transDX(T dx, T dy) const { return T(a) * dx + T(b) * dy; }
|
||||
template <class T> T transDY(T dx, T dy) const { return T(c) * dx + T(d) * dy; }
|
||||
template <class T> T transX(T x, T y) const { return transDX(x, y) + T(pLight->getX()); }
|
||||
template <class T> T transY(T x, T y) const { return transDY(x, y) + T(pLight->getY()); }
|
||||
|
||||
// Landscape coordinate to ray coordinate. Ray coordinates are relative to the light source.
|
||||
template <class T> T rtransDX(T dx, T dy) const { return T(ra) * dx + T(rb) * dy; }
|
||||
template <class T> T rtransDY(T dx, T dy) const { return T(rc) * dx + T(rd) * dy; }
|
||||
template <class T> T rtransX(T x, T y) const { return rtransDX(x-T(pLight->getX()),y-T(pLight->getY())); }
|
||||
template <class T> T rtransY(T x, T y) const { return rtransDY(x-T(pLight->getX()),y-T(pLight->getY())); }
|
||||
|
||||
/** Returns a rectangle in ray coordinates */
|
||||
C4Rect rtransRect(C4Rect r) const {
|
||||
C4Rect Rect(rtransX(r.x, r.y), rtransY(r.x, r.y),
|
||||
rtransDX(r.Wdt, r.Hgt), rtransDY(r.Wdt, r.Hgt));
|
||||
Rect.Normalize();
|
||||
return Rect;
|
||||
}
|
||||
|
||||
bool isConsistent() const;
|
||||
|
||||
/** Returns the Y-position of the given rectangle's left most point when observed from the origin.
|
||||
This function assumes a cartesian coordinate system (y axis up) */
|
||||
int32_t RectLeftMostY(const C4Rect &r) const { return r.x >= 0 ? r.y+r.Hgt : r.y; }
|
||||
/** Returns the Y-position of the given rectangle's right most point when observed from the origin.
|
||||
This function assumes a cartesian coordinate system (y axis up) */
|
||||
int32_t RectRightMostY(const C4Rect &r) const { return r.x + r.Wdt <= 0 ? r.y+r.Hgt : r.y; }
|
||||
|
||||
/** Find right-most ray left of point */
|
||||
C4FoWRay *FindRayLeftOf(int32_t x, int32_t y);
|
||||
/** Find left-most ray to extend over point */
|
||||
C4FoWRay *FindRayOver(int32_t x, int32_t y);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,169 @@
|
|||
|
||||
#include "C4Include.h"
|
||||
#include "C4FoWRay.h"
|
||||
|
||||
// Maximum error allowed while merging rays.
|
||||
const int32_t C4FoWMergeThreshold = 10; // (in landscape pixels * 2)
|
||||
|
||||
// A = 1/2 | a x b |
|
||||
static inline int32_t getDoubleTriangleSurface(int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t x3, int32_t y3)
|
||||
{
|
||||
int32_t ax = x2 - x1, ay = y2 - y1;
|
||||
int32_t bx = x3 - x1, by = y3 - y1;
|
||||
// We don't bother to actually halve so we can stay with integers.
|
||||
// Doesn't matter as long as we keep in mind the threshold needs to
|
||||
// be doubled.
|
||||
return abs(ax * by - ay * bx);
|
||||
}
|
||||
|
||||
StdStrBuf C4FoWRay::getDesc() const {
|
||||
return FormatString("%d:%d@%d:%d%s",
|
||||
getLeftX(1000),
|
||||
getRightX(1000),
|
||||
getLeftEndY(),
|
||||
getRightEndY(),
|
||||
fDirty ? "*" : "");
|
||||
}
|
||||
|
||||
bool C4FoWRay::MergeRight(int x, int y)
|
||||
{
|
||||
// Note: Right-merging is the most common and most important optimization.
|
||||
// This procedure will probably be *hammered* as a result. Worth inlining?
|
||||
|
||||
assert(!isDirty()); assert(isRight(x, y));
|
||||
|
||||
// Calculate error. Note that simply summing up errors is not correct,
|
||||
// strictly speaking (as new and old error surfaces might overlap). Still,
|
||||
// this is quite elaborate already, no need to make it even more
|
||||
int32_t iErr = getDoubleTriangleSurface(
|
||||
getLeftEndX(), iLeftEndY,
|
||||
getRightEndX(), iRightEndY,
|
||||
x, y);
|
||||
if (iError + iErr > C4FoWMergeThreshold)
|
||||
return false;
|
||||
|
||||
// Move right endpoint.
|
||||
iRightX = x;
|
||||
iRightY = y;
|
||||
iRightEndY = y;
|
||||
iError += iErr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C4FoWRay::MergeLeft(int x, int y)
|
||||
{
|
||||
assert(!isDirty()); assert(isLeft(x, y));
|
||||
|
||||
// Calculate error.
|
||||
float iErr = getDoubleTriangleSurface(
|
||||
getLeftEndX(), iLeftEndY,
|
||||
getRightEndX(), iRightEndY,
|
||||
x, y);
|
||||
if (iError + iErr > C4FoWMergeThreshold)
|
||||
return false;
|
||||
|
||||
// Move left endpoint.
|
||||
iLeftX = x;
|
||||
iLeftY = y;
|
||||
iLeftEndY = y;
|
||||
iError += iErr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool C4FoWRay::Eliminate(int x, int y)
|
||||
{
|
||||
// Called on the ray left of the one getting eliminated
|
||||
C4FoWRay *pElim = pNext, *pMerge = pNext->pNext;
|
||||
assert(!!pElim); assert(!!pMerge);
|
||||
assert(!isDirty()); assert(!pMerge->isDirty());
|
||||
|
||||
// Calc errors, add those accumulated on both merged rays
|
||||
int32_t iErr = getDoubleTriangleSurface(
|
||||
getLeftEndX(), iLeftEndY,
|
||||
pMerge->getRightEndX(), pMerge->iLeftEndY,
|
||||
x, y);
|
||||
iErr += iError + pMerge->iError;
|
||||
if (iErr > C4FoWMergeThreshold)
|
||||
return false;
|
||||
|
||||
// Do elimination
|
||||
iRightX = pMerge->iRightX;
|
||||
iRightY = pMerge->iRightY;
|
||||
iRightEndY = pMerge->iRightEndY;
|
||||
pNext = pMerge->pNext;
|
||||
delete pElim;
|
||||
delete pMerge;
|
||||
return true;
|
||||
}
|
||||
|
||||
C4FoWRay *C4FoWRay::Split(int x, int y)
|
||||
{
|
||||
// Make sure to never create negative-surface rays
|
||||
assert(isDirty()); assert(isInside(x, y));
|
||||
|
||||
// Allocate a new ray. Ugh, expensive.
|
||||
C4FoWRay *pRay = new C4FoWRay(x, y, iRightX, iRightY);
|
||||
pRay->Dirty(iLeftEndY);
|
||||
|
||||
// Move to make space
|
||||
iRightX = x;
|
||||
iRightY = y;
|
||||
|
||||
// Relink
|
||||
pRay->pNext = pNext;
|
||||
pNext = pRay;
|
||||
return pRay;
|
||||
}
|
||||
|
||||
void C4FoWRay::MergeDirty()
|
||||
{
|
||||
// As a rule, dirty rays following each other should
|
||||
// always be merged, so splits can be reverted once
|
||||
// the landscape changes.
|
||||
C4FoWRay *pWith = pNext;
|
||||
assert(isDirty()); assert(!!pWith); assert(pWith->isDirty());
|
||||
|
||||
// Figure out how far the new dirty ray reaches. Note that
|
||||
// we might lose information about the landscape here.
|
||||
Dirty(Min(getLeftEndY(), pWith->getLeftEndY()));
|
||||
|
||||
// Set right
|
||||
iRightX = pWith->iRightX;
|
||||
iRightY = pWith->iRightY;
|
||||
|
||||
// Relink & delete
|
||||
pNext = pWith->getNext();
|
||||
delete pWith;
|
||||
}
|
||||
|
||||
void C4FoWRay::Clean(int y)
|
||||
{
|
||||
// Search hit something, this ray is now clean.
|
||||
assert(isDirty());
|
||||
iLeftEndY = y;
|
||||
iRightEndY = y;
|
||||
fDirty = false;
|
||||
}
|
||||
|
||||
void C4FoWRay::Dirty(int y)
|
||||
{
|
||||
// Got invalidated, ray is dirty until updated
|
||||
iLeftEndY = y;
|
||||
iRightEndY = y;
|
||||
fDirty = true;
|
||||
}
|
||||
|
||||
void C4FoWRay::Prune(int32_t y)
|
||||
{
|
||||
// Check which sides we need to prune
|
||||
bool fLeft = (iLeftEndY >= y),
|
||||
fRight = (iRightEndY >= y);
|
||||
// If both sides got pruned, we are clean
|
||||
// (can't possibly extend this ray further)
|
||||
if (fLeft && fRight)
|
||||
Clean(y);
|
||||
else if (fLeft)
|
||||
iLeftEndY = y;
|
||||
if (fRight)
|
||||
iRightEndY = y;
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef C4FOW_H
|
||||
#define C4FOW_H
|
||||
|
||||
#include "StdBuf.h"
|
||||
|
||||
/** This class represents one ray. ...TODO*/
|
||||
class C4FoWRay
|
||||
{
|
||||
public:
|
||||
C4FoWRay(int32_t iLeftX, int32_t iLeftY, int32_t iRightX, int32_t iRightY)
|
||||
: iLeftX(iLeftX), iLeftY(iLeftY), iRightX(iRightX), iRightY(iRightY),
|
||||
iLeftEndY(0), iRightEndY(0),
|
||||
iError(0),
|
||||
fDirty(true),
|
||||
pNext(NULL)
|
||||
{ }
|
||||
|
||||
private:
|
||||
int32_t iLeftX, iLeftY; // left delimiter point
|
||||
int32_t iRightX, iRightY; // right delimiter point
|
||||
int32_t iLeftEndY, iRightEndY; // where it hit solid material. C4FoWRayActive while currently being followed.
|
||||
int32_t iError; // How much error this ray has
|
||||
bool fDirty; // landscape changed since it was followed?
|
||||
C4FoWRay *pNext;
|
||||
|
||||
public:
|
||||
bool isDirty() const { return fDirty; }
|
||||
bool isClean() const { return !fDirty; }
|
||||
C4FoWRay *getNext() const { return pNext; }
|
||||
|
||||
// Get a point on the ray boundary.
|
||||
inline int32_t getLeftX(int32_t y) const { return iLeftX * y / iLeftY; }
|
||||
inline int32_t getRightX(int32_t y) const { return iRightX * y / iRightY; }
|
||||
inline float getLeftXf(int32_t y) const { return (iLeftX * y) / float(iLeftY); }
|
||||
inline float getRightXf(int32_t y) const { return (iRightX * y) / float(iRightY); }
|
||||
// TODO: why is iLeftY or iRightY never 0?
|
||||
|
||||
int32_t getLeftEndY() const { return iLeftEndY; }
|
||||
int32_t getLeftEndX() const { return getLeftX(iLeftEndY); }
|
||||
float getLeftEndXf() const { return getLeftXf(iLeftEndY); }
|
||||
int32_t getRightEndY() const { return iRightEndY; }
|
||||
int32_t getRightEndX() const { return getRightX(iRightEndY); }
|
||||
float getRightEndXf() const { return getRightXf(iRightEndY); }
|
||||
|
||||
StdStrBuf getDesc() const;
|
||||
|
||||
/* returns whether the given point is left of an imaginery line drawn from the left delimiter point to the origin */
|
||||
bool isLeft(int x, int y) const {
|
||||
return iLeftX * y > x * iLeftY;
|
||||
}
|
||||
|
||||
/* returns whether the given point is right of an imaginery line drawn from the right delimiter point to the origin */
|
||||
bool isRight(int x, int y) const {
|
||||
return iRightX * y < x * iRightY;
|
||||
}
|
||||
|
||||
/* returns whether the given point is inside a cone spanned by the ray between the left delimiter and right
|
||||
delimiter point and the ray spanned from the origin to the left delimiter point.
|
||||
|
||||
In case the origin is on the line with the two delimiter points, this function returns whether the point
|
||||
is on the line spanned by the left and right delimiter points. */
|
||||
bool isInside(int x, int y) const {
|
||||
return !isLeft(x, y) && !isRight(x, y);
|
||||
}
|
||||
|
||||
void SetLeft(int x, int y) { iLeftX = x; iLeftY = y; }
|
||||
void SetRight(int x, int y) { iRightX = x; iRightY = y; }
|
||||
|
||||
bool MergeRight(int x, int y);
|
||||
bool MergeLeft(int x, int y);
|
||||
|
||||
/* Split this ray into two: this ray and the returned one. The given point x,y is the position at
|
||||
which the two resulting rays are connected with their left/right endpoints.
|
||||
It is asserted that the given point is on the same line as the delimiting points. */
|
||||
C4FoWRay *Split(int x, int y);
|
||||
/* Remove the two following vertices and connect this ray to the right delimiter point of the next but one ray.
|
||||
The position x,y is only used for the error calcualation and not actually inserted.
|
||||
Returns false and does not do the action in case the error threshold would be reached with these parameters
|
||||
*/
|
||||
// ASK: iError is not added to the error counter of this ray on success
|
||||
// ASK: this removes TWO rays (but the comment says otherwise), why not one?
|
||||
// ASK: the x,y coordinate given is never actually added - even thout it counts towards the error counter
|
||||
bool Eliminate(int x, int y);
|
||||
|
||||
void MergeDirty();
|
||||
|
||||
void Clean(int32_t y);
|
||||
void Dirty(int32_t y);
|
||||
void Prune(int32_t y);
|
||||
|
||||
|
||||
// ASK: why are they called left/right delimiter and not start and endpoints?
|
||||
// ASK: left delimiter, right delimiter, origin always on one line? (if yes: add assertions)
|
||||
|
||||
// TODO: find out more about this DIRTY stuff
|
||||
};
|
||||
|
||||
#endif // C4FOWRAY_H
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
#include "C4Include.h"
|
||||
#include "C4FoWRegion.h"
|
||||
|
||||
bool glCheck() {
|
||||
if (int err = glGetError()) {
|
||||
LogF("GL error %d: %s", err, gluErrorString(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
C4FoWRegion::~C4FoWRegion()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
bool C4FoWRegion::BindFramebuf()
|
||||
{
|
||||
|
||||
// Flip texture
|
||||
C4Surface *pSfc = pSurface;
|
||||
pSurface = pBackSurface;
|
||||
pBackSurface = pSfc;
|
||||
|
||||
// Can simply reuse old texture?
|
||||
if (!pSurface || pSurface->Wdt < Region.Wdt || pSurface->Hgt < Region.Hgt)
|
||||
{
|
||||
// Create texture. Round up to next power of two in order to
|
||||
// prevent rounding errors, as well as preventing lots of
|
||||
// re-allocations when region size changes quickly (think zoom).
|
||||
if (!pSurface)
|
||||
pSurface = new C4Surface();
|
||||
int iWdt = 1, iHgt = 1;
|
||||
while (iWdt < Region.Wdt) iWdt *= 2;
|
||||
while (iHgt < Region.Hgt) iHgt *= 2;
|
||||
if (!pSurface->Create(iWdt, iHgt))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate frame buffer object
|
||||
if (!hFrameBufDraw) {
|
||||
glGenFramebuffersEXT(1, &hFrameBufDraw);
|
||||
glGenFramebuffersEXT(1, &hFrameBufRead);
|
||||
}
|
||||
|
||||
// Bind current texture to frame buffer
|
||||
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, hFrameBufDraw);
|
||||
glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, hFrameBufRead);
|
||||
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT,
|
||||
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
|
||||
pSurface->ppTex[0]->texName, 0);
|
||||
if (pBackSurface)
|
||||
glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT,
|
||||
GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,
|
||||
pBackSurface->ppTex[0]->texName, 0);
|
||||
|
||||
// Check status, unbind if something was amiss
|
||||
GLenum status1 = glCheckFramebufferStatusEXT(GL_READ_FRAMEBUFFER_EXT),
|
||||
status2 = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER_EXT);
|
||||
if (status1 != GL_FRAMEBUFFER_COMPLETE_EXT ||
|
||||
(pBackSurface && status2 != GL_FRAMEBUFFER_COMPLETE_EXT) ||
|
||||
!glCheck())
|
||||
{
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Worked!
|
||||
return true;
|
||||
}
|
||||
|
||||
void C4FoWRegion::Clear()
|
||||
{
|
||||
if (hFrameBufDraw) {
|
||||
glDeleteFramebuffersEXT(1, &hFrameBufDraw);
|
||||
glDeleteFramebuffersEXT(1, &hFrameBufRead);
|
||||
}
|
||||
hFrameBufDraw = hFrameBufRead = 0;
|
||||
delete pSurface; pSurface = NULL;
|
||||
delete pBackSurface; pBackSurface = NULL;
|
||||
}
|
||||
|
||||
void C4FoWRegion::Update(C4Rect r)
|
||||
{
|
||||
// Set the new region
|
||||
Region = r;
|
||||
}
|
||||
|
||||
void C4FoWRegion::Render(const C4TargetFacet *pOnScreen)
|
||||
{
|
||||
// Update FoW at interesting location
|
||||
pFoW->Update(Region);
|
||||
|
||||
// On screen? No need to set up frame buffer - simply shortcut
|
||||
if (pOnScreen)
|
||||
{
|
||||
pFoW->Render(this, pOnScreen);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create & bind the frame buffer
|
||||
pDraw->StorePrimaryClipper();
|
||||
if(!BindFramebuf())
|
||||
{
|
||||
pDraw->RestorePrimaryClipper();
|
||||
return;
|
||||
}
|
||||
assert(pSurface && hFrameBufDraw);
|
||||
if (!pSurface || !hFrameBufDraw)
|
||||
return;
|
||||
|
||||
// Set up a clean context
|
||||
glViewport(0, 0, getSurface()->Wdt, getSurface()->Hgt);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, getSurface()->Wdt, getSurface()->Hgt, 0);
|
||||
|
||||
// Clear texture contents
|
||||
glClearColor(0.0f, 0.5f/1.5f, 0.5f/1.5f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
// Copy over the old state
|
||||
if (OldRegion.Wdt > 0) {
|
||||
|
||||
int dx0 = Region.x - OldRegion.x,
|
||||
dy0 = Region.y - OldRegion.y,
|
||||
dx1 = Region.x + Region.Wdt - OldRegion.x - OldRegion.Wdt,
|
||||
dy1 = Region.y + Region.Hgt - OldRegion.y - OldRegion.Hgt;
|
||||
|
||||
/*glBlitFramebufferEXT(
|
||||
Max(0, dx0), Max(0, -dy1),
|
||||
OldRegion.Wdt - Max(0, -dx1), OldRegion.Hgt - Max(0, dy0),
|
||||
Max(0, -dx0), Max(0, dy1),
|
||||
Region.Wdt - Max(0, dx1), Region.Hgt - Max(0, -dy0),
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
*/
|
||||
|
||||
int sx0 = Max(0, dx0),
|
||||
sy0 = Max(0, dy1),
|
||||
sx1 = OldRegion.Wdt - Max(0, -dx1),
|
||||
sy1 = OldRegion.Hgt - Max(0, -dy0),
|
||||
tx0 = Max(0, -dx0),
|
||||
ty0 = Max(0, -dy1),
|
||||
tx1 = Region.Wdt - Max(0, dx1),
|
||||
ty1 = Region.Hgt - Max(0, dy0);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, getBackSurface()->ppTex[0]->texName);
|
||||
|
||||
glBlendFunc(GL_ONE, GL_ZERO);
|
||||
glBegin(GL_QUADS);
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glTexCoord2f(float(sx0)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy0)/getBackSurface()->Hgt); glVertex2i(tx0, ty0);
|
||||
glTexCoord2f(float(sx0)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy1)/getBackSurface()->Hgt); glVertex2i(tx0, ty1);
|
||||
glTexCoord2f(float(sx1)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy1)/getBackSurface()->Hgt); glVertex2i(tx1, ty1);
|
||||
glTexCoord2f(float(sx1)/getBackSurface()->Wdt,float(getBackSurface()->Hgt-sy0)/getBackSurface()->Hgt); glVertex2i(tx1, ty0);
|
||||
glEnd();
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
||||
glCheck();
|
||||
|
||||
// Fade out. Note we constantly vary the alpha factor all the time -
|
||||
// this is barely visible but makes it a lot less likely that we
|
||||
// hit cases where we add the same thing every time, but still don't
|
||||
// converge to the same color due to rounding.
|
||||
int iAdd = (Game.FrameCounter/3) % 2;
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.0f, 0.5f/1.5f, 0.5f/1.5f, 1.0f/16.0f+iAdd*1.0f/256.0f);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(0, 0);
|
||||
glVertex2i(getSurface()->Wdt, 0);
|
||||
glVertex2i(getSurface()->Wdt, getSurface()->Hgt);
|
||||
glVertex2i(0, getSurface()->Hgt);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
// Render FoW to frame buffer object
|
||||
glBlendFunc(GL_ONE, GL_ONE);
|
||||
pFoW->Render(this);
|
||||
|
||||
// Done!
|
||||
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
pDraw->RestorePrimaryClipper();
|
||||
glCheck();
|
||||
|
||||
OldRegion = Region;
|
||||
|
||||
}
|
||||
|
||||
C4FoWRegion::C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer)
|
||||
: pFoW(pFoW)
|
||||
, pPlayer(pPlayer)
|
||||
, hFrameBufDraw(0), hFrameBufRead(0)
|
||||
, Region(0,0,0,0), OldRegion(0,0,0,0)
|
||||
, pSurface(NULL), pBackSurface(NULL)
|
||||
{
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#ifndef C4FOWREGION_H
|
||||
#define C4FOWREGION_H
|
||||
|
||||
#include "C4Rect.h"
|
||||
#include "C4Surface.h"
|
||||
#include "C4FacetEx.h"
|
||||
#include "C4Player.h"
|
||||
#include "C4FoW.h"
|
||||
|
||||
class C4FoWRegion
|
||||
{
|
||||
public:
|
||||
C4FoWRegion(C4FoW *pFoW, C4Player *pPlayer);
|
||||
~C4FoWRegion();
|
||||
|
||||
private:
|
||||
C4FoW *pFoW;
|
||||
C4Player *pPlayer;
|
||||
C4Rect Region, OldRegion;
|
||||
C4Surface *pSurface, *pBackSurface;
|
||||
GLuint hFrameBufDraw, hFrameBufRead;
|
||||
|
||||
public:
|
||||
const C4Rect &getRegion() const { return Region; }
|
||||
const C4Surface *getSurface() const { return pSurface; }
|
||||
const C4Surface *getBackSurface() const { return pBackSurface; }
|
||||
|
||||
void Clear();
|
||||
|
||||
void Update(C4Rect r);
|
||||
void Render(const C4TargetFacet *pOnScreen = NULL);
|
||||
|
||||
private:
|
||||
bool BindFramebuf();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue