From 1e11df726a585a29a07142d47c1e7e0133f103fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= Date: Fri, 30 Sep 2016 10:13:14 +0300 Subject: [PATCH] Refactor|Renderer|UI: Post-processing shader applied during view composition Instead of having fx::PostProcessing as a lens effect, ViewCompositor now applies the post-processing shader when it blits the game view to the window as one of the layers of the view. This has the benefit that the post-processor doesn't need a separate framebuffer of its own because it can use the existing game view texture and draw directly in the window framebuffer. fx::Resize was also removed because the game view resolution can be trivially changed by resizing the game view texture. --- .../apps/client/include/render/fx/resize.h | 55 ----- .../{render/fx => ui}/postprocessing.h | 21 +- .../apps/client/include/ui/viewcompositor.h | 4 + .../apps/client/src/render/cameralensfx.cpp | 48 +--- doomsday/apps/client/src/render/fx/resize.cpp | 212 ------------------ doomsday/apps/client/src/render/rend_main.cpp | 3 +- .../src/ui/editors/modelasseteditor.cpp | 6 +- .../src/{render/fx => ui}/postprocessing.cpp | 180 +++++++++------ .../apps/client/src/ui/viewcompositor.cpp | 59 ++--- .../apps/client/src/ui/widgets/gamewidget.cpp | 12 +- doomsday/apps/client/src/world/base/map.cpp | 2 +- .../sdk/libgui/include/de/graphics/drawable.h | 1 + doomsday/sdk/libgui/src/graphics/drawable.cpp | 8 +- 13 files changed, 186 insertions(+), 425 deletions(-) delete mode 100644 doomsday/apps/client/include/render/fx/resize.h rename doomsday/apps/client/include/{render/fx => ui}/postprocessing.h (89%) delete mode 100644 doomsday/apps/client/src/render/fx/resize.cpp rename doomsday/apps/client/src/{render/fx => ui}/postprocessing.cpp (61%) diff --git a/doomsday/apps/client/include/render/fx/resize.h b/doomsday/apps/client/include/render/fx/resize.h deleted file mode 100644 index 6569c4276d..0000000000 --- a/doomsday/apps/client/include/render/fx/resize.h +++ /dev/null @@ -1,55 +0,0 @@ -/** @file fx/resize.h Change the size (pixel density) of the view. - * - * @authors Copyright (c) 2014 Jaakko Keränen - * - * @par License - * GPL: http://www.gnu.org/licenses/gpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. You should have received a copy of the GNU - * General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#ifndef DENG_CLIENT_FX_RESIZE_H -#define DENG_CLIENT_FX_RESIZE_H - -#include "render/consoleeffect.h" -#include - -namespace fx { - -/** - * Post-processing of rendered camera lens frames. Maintains an offscreen - * render target and provides a way to draw it back to the regular target - * with shader effects applied. - */ -class Resize : public ConsoleEffect -{ -public: - Resize(int console); - - /** - * Determines whether the effect is active. If it isn't, it can be skipped - * altogether when post processing a frame. - */ - bool isActive() const; - - void glInit(); - void glDeinit(); - - void beginFrame(); - void endFrame(); - -private: - DENG2_PRIVATE(d) -}; - -} // namespace fx - -#endif // DENG_CLIENT_FX_RESIZE_H diff --git a/doomsday/apps/client/include/render/fx/postprocessing.h b/doomsday/apps/client/include/ui/postprocessing.h similarity index 89% rename from doomsday/apps/client/include/render/fx/postprocessing.h rename to doomsday/apps/client/include/ui/postprocessing.h index fa4c840cd0..a821146d13 100644 --- a/doomsday/apps/client/include/render/fx/postprocessing.h +++ b/doomsday/apps/client/include/ui/postprocessing.h @@ -20,19 +20,19 @@ #define DENG_CLIENT_FX_POSTPROCESSING_H #include "render/consoleeffect.h" +#include #include - -namespace fx { +#include /** * Post-processing of rendered camera lens frames. Maintains an offscreen * render target and provides a way to draw it back to the regular target * with shader effects applied. */ -class PostProcessing : public ConsoleEffect +class PostProcessing { public: - PostProcessing(int console); + PostProcessing(); /** * Determines whether the effect is active. If it isn't, it can be skipped @@ -64,14 +64,17 @@ class PostProcessing : public ConsoleEffect void glInit(); void glDeinit(); - void beginFrame(); - void draw(); - void endFrame(); + void update(); + + //void beginFrame(); + void draw(de::Matrix4f const &mvpMatrix, de::GLTexture const &frame); + //void endFrame(); + +public: + static void consoleRegister(); private: DENG2_PRIVATE(d) }; -} // namespace fx - #endif // DENG_CLIENT_FX_POSTPROCESSING_H diff --git a/doomsday/apps/client/include/ui/viewcompositor.h b/doomsday/apps/client/include/ui/viewcompositor.h index 76b1dbb438..4b28535360 100644 --- a/doomsday/apps/client/include/ui/viewcompositor.h +++ b/doomsday/apps/client/include/ui/viewcompositor.h @@ -21,6 +21,8 @@ #include +class PostProcessing; + /** * Compositor for the game view. * @@ -78,6 +80,8 @@ class ViewCompositor */ void drawCompositedLayers(); + PostProcessing &postProcessing(); + private: DENG2_PRIVATE(d) }; diff --git a/doomsday/apps/client/src/render/cameralensfx.cpp b/doomsday/apps/client/src/render/cameralensfx.cpp index bf1550701c..5e06cf1427 100644 --- a/doomsday/apps/client/src/render/cameralensfx.cpp +++ b/doomsday/apps/client/src/render/cameralensfx.cpp @@ -44,8 +44,6 @@ #include "render/fx/bloom.h" #include "render/fx/colorfilter.h" #include "render/fx/lensflares.h" -#include "render/fx/postprocessing.h" -#include "render/fx/resize.h" #include "render/fx/vignette.h" #include "world/p_players.h" @@ -63,46 +61,7 @@ using namespace de; static int fxFramePlayerNum; ///< Player view currently being drawn. #define IDX_LENS_FLARES 3 -#define IDX_POST_PROCESSING 5 - -D_CMD(PostFx) -{ - DENG2_UNUSED(src); - - int console = String(argv[1]).toInt(); - String const shader = argv[2]; - TimeDelta const span = (argc == 4? String(argv[3]).toFloat() : 0); - - if(console < 0 || console >= DDMAXPLAYERS) - { - LOG_SCR_WARNING("Invalid console %i") << console; - return false; - } - - fx::PostProcessing *post = - static_cast(DD_Player(console)->fxStack().effects[IDX_POST_PROCESSING]); - - // Special case to clear out the current shader. - if(shader == "none") - { - post->fadeOut(span); - return true; - } - else if(shader == "opacity") // Change opacity. - { - post->setOpacity(span); - return true; - } - - post->fadeInShader(shader, span); - return true; -} - -void LensFx_Register() -{ - C_CMD("postfx", "is", PostFx); - C_CMD("postfx", "isf", PostFx); -} +//#define IDX_POST_PROCESSING 5 void LensFx_Init() { @@ -110,12 +69,11 @@ void LensFx_Init() { ConsoleEffectStack &stack = DD_Player(i)->fxStack(); stack.effects - << new fx::Resize(i) << new fx::Bloom(i) << new fx::Vignette(i) << new fx::LensFlares(i) // IDX_LENS_FLARES - << new fx::ColorFilter(i) - << new fx::PostProcessing(i); // IDX_POST_PROCESSING + << new fx::ColorFilter(i); + //<< new fx::PostProcessing(i); // IDX_POST_PROCESSING } } diff --git a/doomsday/apps/client/src/render/fx/resize.cpp b/doomsday/apps/client/src/render/fx/resize.cpp deleted file mode 100644 index e67251813e..0000000000 --- a/doomsday/apps/client/src/render/fx/resize.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/** @file fx/resize.cpp Change the size (pixel density) of the view. - * - * @authors Copyright (c) 2014 Jaakko Keränen - * - * @par License - * GPL: http://www.gnu.org/licenses/gpl.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. This program is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General - * Public License for more details. You should have received a copy of the GNU - * General Public License along with this program; if not, see: - * http://www.gnu.org/licenses - */ - -#include "render/fx/resize.h" -#include "ui/clientwindow.h" -#include "clientapp.h" - -#include -#include -#include -#include -#include - -#include - -using namespace de; - -namespace fx { - -static Ranged const FACTOR_RANGE(1.0 / 16.0, 1.0); - -DENG2_PIMPL(Resize) -{ - mutable Variable const *pixelDensity = nullptr; - mutable Variable const *resizeFactor = nullptr; - - GLTextureFramebuffer framebuf; - Drawable frame; - GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; - GLUniform uFrame { "uTex", GLUniform::Sampler2D }; - - typedef GLBufferT VBuf; - - Impl(Public *i) : Base(i) {} - - GuiRootWidget &root() const - { - return ClientWindow::main().game().root(); - } - - void getConfig() const - { - if(!pixelDensity) - { - // Config variables. - pixelDensity = &App::config("render.pixelDensity"); - resizeFactor = &App::config("render.fx.resize.factor"); - } - } - - float factor() const - { - getConfig(); - - double const rf = (*resizeFactor > 0? 1.0 / *resizeFactor : 1.0); - return FACTOR_RANGE.clamp(*pixelDensity * rf); - } - - /// Determines if the post-processing shader will be applied. - bool isActive() const - { - // This kind of scaling is not compatible with Oculus Rift -- LibOVR does its - // own pixel density scaling. - if(ClientApp::vr().mode() == VRConfig::OculusRift) return false; - - return !fequal(factor(), 1.f); - } - - void glInit() - { - framebuf.glInit(); - - uFrame = framebuf.colorTexture(); - - // Drawable for drawing stuff back to the original target. - VBuf *buf = new VBuf; - buf->setVertices(gl::TriangleStrip, - VBuf::Builder().makeQuad(Rectanglef(0, 0, 1, 1), - Rectanglef(0, 1, 1, -1)), - gl::Static); - frame.addBuffer(buf); - - ClientApp::shaders().build(frame.program(), "generic.texture") - << uMvpMatrix << uFrame; - } - - void glDeinit() - { - LOGDEV_GL_XVERBOSE("Releasing GL resources"); - framebuf.glDeinit(); - frame.clear(); - } - - void update() - { - framebuf.resize(GLState::current().target().rectInUse().size() * factor()); - framebuf.setSampleCount(GLTextureFramebuffer::defaultMultisampling()); - } - - void begin() - { - if(!isActive()) return; - - update(); - - GLState::push() - .setTarget(framebuf) - .setViewport(Rectangleui::fromSize(framebuf.size())) - .setColorMask(gl::WriteAll) - .apply(); - framebuf.clear(GLFramebuffer::ColorDepthStencil); - } - - void end() - { - if(!isActive()) return; - - GLState::pop().apply(); - } - - void draw() - { - if(!isActive()) return; - - LIBGUI_GL.glEnable(GL_TEXTURE_2D); - //glDisable(GL_ALPHA_TEST); - - Rectanglef const vp = GLState::current().viewport(); - Vector2f targetSize = GLState::current().target().size(); - - uMvpMatrix = Matrix4f::ortho(vp.left() / targetSize.x, - vp.right() / targetSize.x, - vp.top() / targetSize.y, - vp.bottom() / targetSize.y); - - GLState::push() - .setAlphaTest(false) - .setBlend(false) - .setDepthTest(false) - .apply(); - - frame.draw(); - - GLState::pop().apply(); - - //glEnable(GL_ALPHA_TEST); - LIBGUI_GL.glDisable(GL_TEXTURE_2D); - //glEnable(GL_BLEND); - } -}; - -Resize::Resize(int console) - : ConsoleEffect(console), d(new Impl(this)) -{} - -bool Resize::isActive() const -{ - return d->isActive(); -} - -void Resize::glInit() -{ - if(!d->isActive()) return; - - LOG_AS("fx::Resize"); - - ConsoleEffect::glInit(); - d->glInit(); -} - -void Resize::glDeinit() -{ - LOG_AS("fx::Resize"); - - d->glDeinit(); - ConsoleEffect::glDeinit(); -} - -void Resize::beginFrame() -{ - d->begin(); -} - -void Resize::endFrame() -{ - LOG_AS("fx::Resize"); - - d->end(); - d->draw(); - - if(!d->isActive() && isInited()) - { - glDeinit(); - } -} - -} // namespace fx diff --git a/doomsday/apps/client/src/render/rend_main.cpp b/doomsday/apps/client/src/render/rend_main.cpp index 2b51e699e1..0ab53afc37 100644 --- a/doomsday/apps/client/src/render/rend_main.cpp +++ b/doomsday/apps/client/src/render/rend_main.cpp @@ -79,6 +79,7 @@ #include "ui/editors/rendererappearanceeditor.h" #include "ui/editors/modelasseteditor.h" #include "ui/ui_main.h" +#include "ui/postprocessing.h" #include "ui/editors/edit_bias.h" @@ -6412,7 +6413,7 @@ void Rend_Register() Rend_RadioRegister(); Rend_SpriteRegister(); - LensFx_Register(); + PostProcessing::consoleRegister(); fx::Bloom::consoleRegister(); fx::Vignette::consoleRegister(); fx::LensFlares::consoleRegister(); diff --git a/doomsday/apps/client/src/ui/editors/modelasseteditor.cpp b/doomsday/apps/client/src/ui/editors/modelasseteditor.cpp index b8d7737721..21e7dd3046 100644 --- a/doomsday/apps/client/src/ui/editors/modelasseteditor.cpp +++ b/doomsday/apps/client/src/ui/editors/modelasseteditor.cpp @@ -224,7 +224,7 @@ DENG_GUI_PIMPL(ModelAssetEditor) int const idNum = idNumber(); if (isWeaponAsset()) { - auto &weaponAnim = ClientApp::players().at(idNum).as().playerWeaponAnimator(); + auto &weaponAnim = ClientApp::player(idNum).playerWeaponAnimator(); if (weaponAnim.hasModel()) { return &weaponAnim.animator(); @@ -485,7 +485,7 @@ DENG_GUI_PIMPL(ModelAssetEditor) { for (int idx = 0; idx < ClientApp::players().count(); ++idx) { - auto &client = ClientApp::players().at(idx).as(); + auto &client = ClientApp::player(idx); auto &anim = client.playerWeaponAnimator(); if (anim.hasModel()) { @@ -572,7 +572,7 @@ DENG_GUI_PIMPL(ModelAssetEditor) if (isWeaponAsset()) { - auto &weapon = ClientApp::players().at(data.mobjId).as().playerWeaponAnimator(); + auto &weapon = ClientApp::player(data.mobjId).playerWeaponAnimator(); if (weapon.hasModel()) animator = &weapon.animator(); } else diff --git a/doomsday/apps/client/src/render/fx/postprocessing.cpp b/doomsday/apps/client/src/ui/postprocessing.cpp similarity index 61% rename from doomsday/apps/client/src/render/fx/postprocessing.cpp rename to doomsday/apps/client/src/ui/postprocessing.cpp index 1f90acea16..7c860df669 100644 --- a/doomsday/apps/client/src/render/fx/postprocessing.cpp +++ b/doomsday/apps/client/src/ui/postprocessing.cpp @@ -16,9 +16,11 @@ * http://www.gnu.org/licenses */ -#include "render/fx/postprocessing.h" +#include "ui/postprocessing.h" #include "ui/clientwindow.h" +#include "ui/viewcompositor.h" #include "clientapp.h" +#include "clientplayer.h" #include #include @@ -29,17 +31,19 @@ using namespace de; -namespace fx { +D_CMD(PostFx); DENG2_PIMPL(PostProcessing) { - GLTextureFramebuffer framebuf; + enum { PassThroughProgram = 0, ActiveProgram = 1 }; + + //GLTextureFramebuffer framebuf; Drawable frame; - GLUniform uMvpMatrix; - GLUniform uFrame; - GLUniform uFadeInOut; - Animation fade; - float opacity; + GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; + GLUniform uFrame { "uTex", GLUniform::Sampler2D }; + GLUniform uFadeInOut { "uFadeInOut", GLUniform::Float }; + Animation fade { 0, Animation::Linear }; + float opacity { 1.f }; struct QueueEntry { String shaderName; @@ -54,44 +58,37 @@ DENG2_PIMPL(PostProcessing) typedef GLBufferT VBuf; - Impl(Public *i) - : Base(i) - , uMvpMatrix("uMvpMatrix", GLUniform::Mat4) - , uFrame ("uTex", GLUniform::Sampler2D) - , uFadeInOut("uFadeInOut", GLUniform::Float) - , fade(0, Animation::Linear) - , opacity(1.f) + Impl(Public *i) : Base(i) {} - GuiRootWidget &root() const + ~Impl() { - return ClientWindow::main().game().root(); + DENG2_ASSERT(!frame.isReady()); // deinited earlier } -#if 0 - Vector2ui consoleSize() const + /*GuiRootWidget &root() const + { + return ClientWindow::main().game().root(); + }*/ + + void attachUniforms(GLProgram &program) { - /** - * @todo The offscreen target should simply use the viewport area, not - * the full canvas size. This way the shader could, for instance, - * easily mirror texture coordinates. However, this would require - * drawing the frame without applying a further GL viewport in the game - * widgets. -jk - */ - //return self.viewRect().size(); - return root().window().canvas().size(); + program << uMvpMatrix << uFrame << uFadeInOut; } -#endif bool setShader(String const &name) { try { - self.shaders().build(frame.program(), "fx.post." + name); + auto &shaders = ClientApp::shaders(); + GLProgram &prog = frame.addProgram(ActiveProgram); + shaders.build(prog, "fx.post." + name); + attachUniforms(prog); + frame.setProgram(prog); LOG_GL_MSG("Post-processing shader \"fx.post.%s\"") << name; return true; } - catch(Error const &er) + catch (Error const &er) { LOG_GL_WARNING("Failed to set shader to \"fx.post.%s\":\n%s") << name << er.asText(); @@ -107,13 +104,15 @@ DENG2_PIMPL(PostProcessing) void glInit() { + if (frame.isReady()) return; + //LOG_DEBUG("Allocating texture and target, size %s") << consoleSize().asText(); - framebuf.glInit(); + //framebuf.glInit(); //framebuf.setColorFormat(Image::RGBA_8888); //framebuf.resize(consoleSize()); - uFrame = framebuf.colorTexture(); + //uFrame = framebuf.colorTexture(); // Drawable for drawing stuff back to the original target. VBuf *buf = new VBuf; @@ -122,32 +121,38 @@ DENG2_PIMPL(PostProcessing) Rectanglef(0, 1, 1, -1)), gl::Static); frame.addBuffer(buf); - frame.program() << uMvpMatrix << uFrame << uFadeInOut; + + // The default program is a pass-through shader. + ClientApp::shaders().build(frame.program(), "generic.texture"); + attachUniforms(frame.program()); } void glDeinit() { + if (!frame.isReady()) return; + LOGDEV_GL_XVERBOSE("Releasing GL resources"); - framebuf.glDeinit(); + //framebuf.glDeinit(); + frame.clear(); } - void update() + /*void update() { framebuf.resize(GLState::current().target().rectInUse().size()); framebuf.setSampleCount(GLTextureFramebuffer::defaultMultisampling()); - } + }*/ void checkQueue() { // An ongoing fade? - if(!fade.done()) return; // Let's check back later. + if (!fade.done()) return; // Let's check back later. - if(!queue.isEmpty()) + if (!queue.isEmpty()) { QueueEntry entry = queue.takeFirst(); - if(!entry.shaderName.isEmpty()) + if (!entry.shaderName.isEmpty()) { - if(!setShader(entry.shaderName)) + if (!setShader(entry.shaderName)) { fade = 0; return; @@ -158,9 +163,9 @@ DENG2_PIMPL(PostProcessing) } } - void begin() +/* void begin() { - if(!isActive()) return; + if (!isActive()) return; update(); @@ -170,18 +175,24 @@ DENG2_PIMPL(PostProcessing) .setColorMask(gl::WriteAll) .apply(); framebuf.clear(GLFramebuffer::ColorDepthStencil); - } + }*/ - void end() + /*void end() { - if(!isActive()) return; + if (!isActive()) return; GLState::pop().apply(); - } + }*/ void draw() { - if(!isActive()) return; + if (isActive()) + { + uFadeInOut = fade * opacity; + } + frame.draw(); + +#if 0 LIBGUI_GL.glEnable(GL_TEXTURE_2D); //glDisable(GL_ALPHA_TEST); @@ -194,7 +205,6 @@ DENG2_PIMPL(PostProcessing) vp.top() / targetSize.y, vp.bottom() / targetSize.y); - uFadeInOut = fade * opacity; GLState::push() .setAlphaTest(false) @@ -209,11 +219,12 @@ DENG2_PIMPL(PostProcessing) //glEnable(GL_ALPHA_TEST); LIBGUI_GL.glDisable(GL_TEXTURE_2D); //glEnable(GL_BLEND); +#endif } }; -PostProcessing::PostProcessing(int console) - : ConsoleEffect(console), d(new Impl(this)) +PostProcessing::PostProcessing() + : d(new Impl(this)) {} bool PostProcessing::isActive() const @@ -238,43 +249,72 @@ void PostProcessing::setOpacity(float opacity) void PostProcessing::glInit() { - if(!d->isActive()) return; - - LOG_AS("fx::PostProcessing"); - - ConsoleEffect::glInit(); + LOG_AS("PostProcessing"); d->glInit(); } void PostProcessing::glDeinit() { - LOG_AS("fx::PostProcessing"); - + LOG_AS("PostProcessing"); d->glDeinit(); - ConsoleEffect::glDeinit(); } -void PostProcessing::beginFrame() +void PostProcessing::update() { - d->begin(); + LOG_AS("PostProcessing"); + + if (d->isActive()) + { + d->checkQueue(); + } + else + { + d->frame.setProgram(Impl::PassThroughProgram); + } } -void PostProcessing::draw() +void PostProcessing::draw(Matrix4f const &mvpMatrix, GLTexture const &frame) { - d->end(); + d->uMvpMatrix = mvpMatrix; + d->uFrame = frame; + d->draw(); } -void PostProcessing::endFrame() +void PostProcessing::consoleRegister() // static +{ + C_CMD("postfx", "is", PostFx); + C_CMD("postfx", "isf", PostFx); +} + +D_CMD(PostFx) { - LOG_AS("fx::PostProcessing"); + DENG2_UNUSED(src); + + int console = String(argv[1]).toInt(); + String const shader = argv[2]; + TimeDelta const span = (argc == 4? String(argv[3]).toDouble() : 0); - if(!d->isActive() && isInited()) + if (console < 0 || console >= DDMAXPLAYERS) { - glDeinit(); + LOG_SCR_WARNING("Invalid console %i") << console; + return false; } - d->checkQueue(); -} + PostProcessing &post = ClientApp::player(console).viewCompositor().postProcessing(); + + // Special case to clear out the current shader. + if (shader == "none") + { + post.fadeOut(span); + return true; + } + else if (shader == "opacity") // Change opacity. + { + post.setOpacity(float(span)); + return true; + } -} // namespace fx + post.fadeInShader(shader, span); + return true; +} diff --git a/doomsday/apps/client/src/ui/viewcompositor.cpp b/doomsday/apps/client/src/ui/viewcompositor.cpp index 85ad821fe1..5a58a0b725 100644 --- a/doomsday/apps/client/src/ui/viewcompositor.cpp +++ b/doomsday/apps/client/src/ui/viewcompositor.cpp @@ -18,6 +18,7 @@ #include "ui/viewcompositor.h" #include "ui/clientwindow.h" +#include "ui/postprocessing.h" #include "ui/infine/finaleinterpreter.h" #include "ui/infine/finalepagewidget.h" #include "ui/editors/edit_bias.h" @@ -51,20 +52,16 @@ DENG2_PIMPL(ViewCompositor) /// game HUD. GLTextureFramebuffer viewFramebuf; - Drawable frameDrawable; - GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; - GLUniform uFrameTex { "uTex", GLUniform::Sampler2D }; + PostProcessing postProcessing; + //Drawable frameDrawable; + //GLUniform uMvpMatrix { "uMvpMatrix", GLUniform::Mat4 }; + //GLUniform uFrameTex { "uTex", GLUniform::Sampler2D }; Impl(Public *i) : Base(i) , viewFramebuf(Image::RGBA_8888) {} - ~Impl() - { - DENG2_ASSERT(!frameDrawable.isReady()); // deinited earlier - } - void getConfig() const { if (!pixelDensity) @@ -98,7 +95,9 @@ DENG2_PIMPL(ViewCompositor) viewFramebuf.resize(framebufferSize()); viewFramebuf.glInit(); - if (!frameDrawable.isReady()) + postProcessing.glInit(); + + /*if (!frameDrawable.isReady()) { ClientApp::shaders().build(frameDrawable.program(), "generic.texture") << uMvpMatrix @@ -112,22 +111,13 @@ DENG2_PIMPL(ViewCompositor) VBuf::Builder verts; verts.makeQuad(Rectanglef(0, 0, 1, 1), Rectanglef(0, 1, 1, -1)); vbuf->setVertices(gl::TriangleStrip, verts, gl::Static); - } + }*/ } void glDeinit() { viewFramebuf.glDeinit(); - frameDrawable.clear(); - } - - static void setupProjectionForFinale(dgl_borderedprojectionstate_t *bp) - { - GL_ConfigureBorderedProjection(bp, BPF_OVERDRAW_CLIP | - (!App_World().hasMap()? BPF_OVERDRAW_MASK : 0), - SCREENWIDTH, SCREENHEIGHT, - DENG_GAMEVIEW_WIDTH, DENG_GAMEVIEW_HEIGHT, - scalemode_t(Con_GetByte("rend-finale-stretch"))); + postProcessing.glDeinit(); } }; @@ -175,8 +165,6 @@ GLTextureFramebuffer const &ViewCompositor::gameView() const void ViewCompositor::drawCompositedLayers() { - DENG2_ASSERT(d->frameDrawable.isReady()); - GLState::push() .setAlphaTest(false) .setBlend (false) @@ -192,12 +180,21 @@ void ViewCompositor::drawCompositedLayers() R_UseViewPort(d->playerNum); // 3D world view (using the previously rendered texture). - { + //if (d->frameDrawable.isReady()) + //{ + // Set up the appropriate post-processing shader. + +/* d->uFrameTex = d->viewFramebuf.colorTexture(); d->uMvpMatrix = ClientWindow::main().root().projMatrix2D() * Matrix4f::scaleThenTranslate(view3D.size(), view3D.topLeft); - d->frameDrawable.draw(); - } + d->frameDrawable.draw();*/ +// } + + d->postProcessing.update(); + d->postProcessing.draw(ClientWindow::main().root().projMatrix2D() * + Matrix4f::scaleThenTranslate(view3D.size(), view3D.topLeft), + d->viewFramebuf.colorTexture()); // Some of the layers use OpenGL 2 drawing code. DGL_MatrixMode(DGL_PROJECTION); @@ -206,6 +203,7 @@ void ViewCompositor::drawCompositedLayers() // Fill around a scaled-down 3D view. The border is not visible if the 3D view // covers the entire area. + //if (d->frameDrawable.isReady()) { R_RenderPlayerViewBorder(); } @@ -244,7 +242,11 @@ void ViewCompositor::drawCompositedLayers() if (App_InFineSystem().finaleInProgess()) { dgl_borderedprojectionstate_t bp; - d->setupProjectionForFinale(&bp); + GL_ConfigureBorderedProjection(&bp, BPF_OVERDRAW_CLIP | + (!App_World().hasMap()? BPF_OVERDRAW_MASK : 0), + SCREENWIDTH, SCREENHEIGHT, + DENG_GAMEVIEW_WIDTH, DENG_GAMEVIEW_HEIGHT, + scalemode_t(Con_GetByte("rend-finale-stretch"))); GL_BeginBorderedProjection(&bp); for (Finale *finale : App_InFineSystem().finales()) { @@ -286,3 +288,8 @@ void ViewCompositor::drawCompositedLayers() GLState::considerNativeStateUndefined(); GLState::pop().apply(); } + +PostProcessing &ViewCompositor::postProcessing() +{ + return d->postProcessing; +} diff --git a/doomsday/apps/client/src/ui/widgets/gamewidget.cpp b/doomsday/apps/client/src/ui/widgets/gamewidget.cpp index c2ffb415ba..f715ca9980 100644 --- a/doomsday/apps/client/src/ui/widgets/gamewidget.cpp +++ b/doomsday/apps/client/src/ui/widgets/gamewidget.cpp @@ -73,7 +73,7 @@ DENG2_PIMPL(GameWidget) */ void renderGameViews() { - ClientApp::app().forLocalPlayers([] (ClientPlayer &player) + ClientApp::forLocalPlayers([] (ClientPlayer &player) { player.viewCompositor().renderGameView([] (int playerNum) { R_RenderViewPort(playerNum); @@ -89,11 +89,19 @@ DENG2_PIMPL(GameWidget) */ void drawCompositedFrames() { - ClientApp::app().forLocalPlayers([this] (ClientPlayer &player) + int numLocal = 0; + ClientApp::forLocalPlayers([this, &numLocal] (ClientPlayer &player) { player.viewCompositor().drawCompositedLayers(); + ++numLocal; return LoopContinue; }); + + // Nobody is playing right now? + if (numLocal == 0) + { + ClientApp::player(0).viewCompositor().drawCompositedLayers(); + } } void draw() diff --git a/doomsday/apps/client/src/world/base/map.cpp b/doomsday/apps/client/src/world/base/map.cpp index 8f40134e3d..17f20f96ce 100644 --- a/doomsday/apps/client/src/world/base/map.cpp +++ b/doomsday/apps/client/src/world/base/map.cpp @@ -2571,7 +2571,7 @@ void Map::link(mobj_t &mob, dint flags) // If this is a player - perform additional tests to see if they have either entered or exited the void. if (mob.dPlayer && mob.dPlayer->mo) { - auto &client = ClientApp::players().at(P_GetDDPlayerIdx(mob.dPlayer)).as(); + auto &client = ClientApp::player(P_GetDDPlayerIdx(mob.dPlayer)); client.inVoid = true; if (Mobj_HasSubsector(mob)) { diff --git a/doomsday/sdk/libgui/include/de/graphics/drawable.h b/doomsday/sdk/libgui/include/de/graphics/drawable.h index c57a2737c2..cfbb73deef 100644 --- a/doomsday/sdk/libgui/include/de/graphics/drawable.h +++ b/doomsday/sdk/libgui/include/de/graphics/drawable.h @@ -236,6 +236,7 @@ class LIBGUI_PUBLIC Drawable : public AssetGroup */ void setProgram(GLProgram &program); + void setProgram(Id programId); void setProgram(Name const &programName); /** diff --git a/doomsday/sdk/libgui/src/graphics/drawable.cpp b/doomsday/sdk/libgui/src/graphics/drawable.cpp index dd87b329db..51504109bd 100644 --- a/doomsday/sdk/libgui/src/graphics/drawable.cpp +++ b/doomsday/sdk/libgui/src/graphics/drawable.cpp @@ -13,7 +13,7 @@ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. You should have received a copy of * the GNU Lesser General Public License along with this program; if not, see: - * http://www.gnu.org/licenses + * http://www.gnu.org/licenses */ #include "de/Drawable" @@ -197,6 +197,7 @@ GLProgram &Drawable::program(Name const &programName) const Drawable::Id Drawable::programId(Name const &programName) const { + if (programName.isEmpty()) return 0; // Default program. DENG2_ASSERT(d->programNames.contains(programName)); return d->programNames[programName]; } @@ -411,6 +412,11 @@ void Drawable::setProgram(GLProgram &program) } } +void Drawable::setProgram(Id programId) +{ + setProgram(program(programId)); +} + void Drawable::setProgram(Name const &programName) { setProgram(program(programName));