diff --git a/doomsday/client/data/shaders.dei b/doomsday/client/data/shaders.dei index 9124c622be..8bc4967e3d 100644 --- a/doomsday/client/data/shaders.dei +++ b/doomsday/client/data/shaders.dei @@ -156,6 +156,11 @@ group fx { } } + shader lensflares { + path.vertex = "shaders/lensflares.vsh" + path.fragment = "shaders/lensflares.fsh" + } + # Post-processing shaders need to have uFadeInOut (0..1) for # fading the effect in/out. group post { diff --git a/doomsday/client/data/shaders/lensflares.fsh b/doomsday/client/data/shaders/lensflares.fsh new file mode 100644 index 0000000000..3ad8f308dd --- /dev/null +++ b/doomsday/client/data/shaders/lensflares.fsh @@ -0,0 +1,17 @@ +uniform sampler2D uTex; +varying highp vec2 vUV; + +void main(void) { + gl_FragColor = vec4(1.0, 0.0, 1.0, 1.0); + /* + highp vec4 original = texture2D(uTex, vUV); + highp float intens = + (0.2125 * original.r) + + (0.7154 * original.g) + + (0.0721 * original.b); + gl_FragColor = vec4(vec3(intens), 1.0); + if(uFadeInOut < 1.0) { + gl_FragColor = mix(original, gl_FragColor, uFadeInOut); + } + */ +} diff --git a/doomsday/client/data/shaders/lensflares.vsh b/doomsday/client/data/shaders/lensflares.vsh new file mode 100644 index 0000000000..d32ad52ada --- /dev/null +++ b/doomsday/client/data/shaders/lensflares.vsh @@ -0,0 +1,11 @@ +uniform highp mat4 uMvpMatrix; +uniform highp vec2 uViewUnit; +attribute highp vec4 aVertex; +attribute highp vec2 aUV; +attribute highp vec2 aUV2; +varying highp vec2 vUV; + +void main(void) { + gl_Position = (uMvpMatrix * aVertex) + vec4(aUV2 * uViewUnit, 0, 0); + vUV = aUV; +} diff --git a/doomsday/client/include/render/fx/lensflares.h b/doomsday/client/include/render/fx/lensflares.h index a6118adf1c..faa81dbc76 100644 --- a/doomsday/client/include/render/fx/lensflares.h +++ b/doomsday/client/include/render/fx/lensflares.h @@ -20,6 +20,7 @@ #define DENG_CLIENT_FX_LENSFLARES_H #include "render/consoleeffect.h" +#include "render/ilightsource.h" namespace fx { @@ -31,9 +32,13 @@ class LensFlares : public ConsoleEffect public: LensFlares(int console); + void clearLights(); + void markLightPotentiallyVisibleForCurrentFrame(ILightSource const *lightSource); + void glInit(); void glDeinit(); + void beginFrame(); void draw(); private: diff --git a/doomsday/client/include/render/ilightsource.h b/doomsday/client/include/render/ilightsource.h index 51b40ece61..55e2d1719b 100644 --- a/doomsday/client/include/render/ilightsource.h +++ b/doomsday/client/include/render/ilightsource.h @@ -45,7 +45,7 @@ class ILightSource /** * RGB color of the emitted light. */ - typedef de::Vector3f Color; + typedef de::Vector3f Colorf; public: virtual ~ILightSource() {} @@ -68,7 +68,7 @@ class ILightSource * not be factored into the color values, but is instead returned separately * by lightSourceIntensity(). */ - virtual Color lightSourceColor() const = 0; + virtual Colorf lightSourceColorf() const = 0; /** * Returns the intensity of the light. diff --git a/doomsday/client/src/render/fx/lensflares.cpp b/doomsday/client/src/render/fx/lensflares.cpp index 87b7a43207..e16963c006 100644 --- a/doomsday/client/src/render/fx/lensflares.cpp +++ b/doomsday/client/src/render/fx/lensflares.cpp @@ -17,12 +17,17 @@ */ #include "render/fx/lensflares.h" +#include "render/ilightsource.h" +#include "render/viewports.h" #include "gl/gl_main.h" #include +#include #include #include +#include + using namespace de; namespace fx { @@ -32,6 +37,8 @@ namespace fx { */ struct FlareData { + AtlasTexture atlas; + FlareData() { DENG_ASSERT_IN_MAIN_THREAD(); @@ -52,30 +59,165 @@ DENG2_PIMPL(LensFlares) typedef Shared SharedFlareData; SharedFlareData *res; - Instance(Public *i) : Base(i), res(0) + struct TestLight : public ILightSource + { + public: + LightId lightSourceId() const { + return 1; + } + Origin lightSourceOrigin() const { + return Origin(1055, -3280, 30); + } + dfloat lightSourceRadius() const { + return 10; + } + Colorf lightSourceColorf() const { + return Colorf(1, 1, 1); + } + dfloat lightSourceIntensity(de::Vector3d const &) const { + return 1; + } + }; + TestLight testLight; + + /** + * Current state of a potentially visible light. + */ + struct PVLight + { + ILightSource const *light; + int seenFrame; // R_FrameCount() + + PVLight() : light(0), seenFrame(0) + {} + }; + + typedef QHash PVSet; + PVSet pvs; + + typedef GLBufferT VBuf; + VBuf *buffer; + Drawable drawable; + GLUniform uMvpMatrix; + GLUniform uViewUnit; + + Instance(Public *i) + : Base(i) + , res(0) + , buffer(0) + , uMvpMatrix("uMvpMatrix", GLUniform::Mat4) + , uViewUnit ("uViewUnit", GLUniform::Vec2) {} ~Instance() { DENG2_ASSERT(res == 0); // should have been deinited releaseRef(res); + clearPvs(); } void glInit() { // Acquire a reference to the shared flare data. res = SharedFlareData::hold(); + + buffer = new VBuf; + drawable.addBuffer(buffer); + self.shaders().build(drawable.program(), "fx.lensflares") + << uMvpMatrix << uViewUnit; } void glDeinit() { + drawable.clear(); + buffer = 0; + clearPvs(); releaseRef(res); } + + void clearPvs() + { + qDeleteAll(pvs); + pvs.clear(); + } + + void addToPvs(ILightSource const *light) + { + PVSet::iterator found = pvs.find(light->lightSourceId()); + if(found == pvs.end()) + { + found = pvs.insert(light->lightSourceId(), new PVLight); + } + + PVLight *pvl = found.value(); + pvl->light = light; + pvl->seenFrame = R_FrameCount(); + } + + void makeVerticesForPVS() + { + int const thisFrame = R_FrameCount(); + + // The vertex buffer will contain a number of quads. + VBuf::Vertices verts; + VBuf::Indices idx; + VBuf::Type vtx; + + for(PVSet::const_iterator i = pvs.constBegin(); i != pvs.constEnd(); ++i) + { + PVLight const *pvl = i.value(); + + // Skip lights that are not visible right now. + /// @todo If so, it might be time to purge it from the PVS. + if(pvl->seenFrame != thisFrame) continue; + + float radius = .1f; + Rectanglef uvRect; + + int const firstIdx = verts.size(); + + vtx.pos = pvl->light->lightSourceOrigin().xzy(); + vtx.rgba = pvl->light->lightSourceColorf(); + + vtx.texCoord[0] = uvRect.topLeft; + vtx.texCoord[1] = Vector2f(-1, -1) * radius; + verts << vtx; + + vtx.texCoord[0] = uvRect.topRight(); + vtx.texCoord[1] = Vector2f(1, -1) * radius; + verts << vtx; + + vtx.texCoord[0] = uvRect.bottomRight; + vtx.texCoord[1] = Vector2f(1, 1) * radius; + verts << vtx; + + vtx.texCoord[0] = uvRect.bottomLeft(); + vtx.texCoord[1] = Vector2f(-1, 1) * radius; + verts << vtx; + + // Make two triangles. + idx << firstIdx << firstIdx + 2 << firstIdx + 1 + << firstIdx << firstIdx + 2 << firstIdx + 3; + } + + buffer->setVertices(verts, gl::Dynamic); + buffer->setIndices(gl::Triangles, idx, gl::Dynamic); + } }; LensFlares::LensFlares(int console) : ConsoleEffect(console), d(new Instance(this)) {} +void LensFlares::clearLights() +{ + d->clearPvs(); +} + +void LensFlares::markLightPotentiallyVisibleForCurrentFrame(ILightSource const *lightSource) +{ + d->addToPvs(lightSource); +} + void LensFlares::glInit() { LOG_AS("fx::LensFlares"); @@ -92,9 +234,27 @@ void LensFlares::glDeinit() ConsoleEffect::glDeinit(); } +void fx::LensFlares::beginFrame() +{ + markLightPotentiallyVisibleForCurrentFrame(&d->testLight); // testing + + d->makeVerticesForPVS(); +} + void LensFlares::draw() { + //Rectanglef const rect = viewRect(); + + d->uViewUnit = Vector2f(1, 1); + d->uMvpMatrix = GL_GetProjectionMatrix(); + + GLState::push() + .setCull(gl::None) + .setDepthTest(false); + + d->drawable.draw(); + GLState::pop().apply(); } } // namespace fx