]> git.lizzy.rs Git - minetest.git/commitdiff
Implement rendering pipeline and post-processing (#12465)
authorx2048 <codeforsmile@gmail.com>
Tue, 6 Sep 2022 06:25:18 +0000 (08:25 +0200)
committerGitHub <noreply@github.com>
Tue, 6 Sep 2022 06:25:18 +0000 (08:25 +0200)
Co-authored-by: Lars Mueller <appgurulars@gmx.de>
Co-authored-by: sfan5 <sfan5@live.de>
Co-authored-by: lhofhansl <lhofhansl@yahoo.com>
33 files changed:
client/shaders/default_shader/opengl_fragment.glsl
client/shaders/nodes_shader/opengl_fragment.glsl
client/shaders/nodes_shader/opengl_vertex.glsl
client/shaders/object_shader/opengl_fragment.glsl
client/shaders/second_stage/opengl_fragment.glsl [new file with mode: 0644]
client/shaders/second_stage/opengl_vertex.glsl [new file with mode: 0644]
client/shaders/selection_shader/opengl_fragment.glsl
src/client/CMakeLists.txt
src/client/camera.cpp
src/client/hud.cpp
src/client/render/anaglyph.cpp
src/client/render/anaglyph.h
src/client/render/core.cpp
src/client/render/core.h
src/client/render/factory.cpp
src/client/render/interlaced.cpp
src/client/render/interlaced.h
src/client/render/pageflip.cpp [deleted file]
src/client/render/pageflip.h [deleted file]
src/client/render/pipeline.cpp [new file with mode: 0644]
src/client/render/pipeline.h [new file with mode: 0644]
src/client/render/plain.cpp
src/client/render/plain.h
src/client/render/secondstage.cpp [new file with mode: 0644]
src/client/render/secondstage.h [new file with mode: 0644]
src/client/render/sidebyside.cpp
src/client/render/sidebyside.h
src/client/render/stereo.cpp
src/client/render/stereo.h
src/client/shader.cpp
src/client/shader.h
src/client/shadows/dynamicshadowsrender.cpp
src/client/shadows/dynamicshadowsrender.h

index 5018ac6ea39dc27a831d2a5275fc1021683aa52d..300c0c5897b83eea26782a15d4b99aedd7931e73 100644 (file)
@@ -2,5 +2,5 @@ varying lowp vec4 varColor;
 
 void main(void)
 {
-       gl_FragColor = varColor;
+       gl_FragData[0] = varColor;
 }
index c4b947e72a0e07e76e742bda83b01237346885e7..1f7d98069d7f6fffa2ccd2cae0674145ed2a0b48 100644 (file)
@@ -45,6 +45,9 @@ centroid varying vec2 varTexCoord;
 #endif
 varying vec3 eyeVec;
 varying float nightRatio;
+varying vec3 tsEyeVec;
+varying vec3 lightVec;
+varying vec3 tsLightVec;
 
 const float fogStart = FOG_START;
 const float fogShadingParameter = 1.0 / ( 1.0 - fogStart);
@@ -359,40 +362,6 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 #endif
 #endif
 
-#if ENABLE_TONE_MAPPING
-
-/* Hable's UC2 Tone mapping parameters
-       A = 0.22;
-       B = 0.30;
-       C = 0.10;
-       D = 0.20;
-       E = 0.01;
-       F = 0.30;
-       W = 11.2;
-       equation used:  ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
-*/
-
-vec3 uncharted2Tonemap(vec3 x)
-{
-       return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
-}
-
-vec4 applyToneMapping(vec4 color)
-{
-       color = vec4(pow(color.rgb, vec3(2.2)), color.a);
-       const float gamma = 1.6;
-       const float exposureBias = 5.5;
-       color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
-       // Precalculated white_scale from
-       //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
-       vec3 whiteScale = vec3(1.036015346);
-       color.rgb *= whiteScale;
-       return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
-}
-#endif
-
-
-
 void main(void)
 {
        vec3 color;
@@ -470,10 +439,6 @@ void main(void)
        }
 #endif
 
-#if ENABLE_TONE_MAPPING
-       col = applyToneMapping(col);
-#endif
-
        // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?),
        // the fog will only be rendered correctly if the last operation before the
        // clamp() is an addition. Else, the clamp() seems to be ignored.
@@ -488,5 +453,5 @@ void main(void)
        col = mix(skyBgColor, col, clarity);
        col = vec4(col.rgb, base.a);
 
-       gl_FragColor = col;
+       gl_FragData[0] = col;
 }
index 40f0965e1027ebf0a1eb1d879ab238cdd73f2652..42fae7612a67800eff357ffbe98b8fbd9ea32d6c 100644 (file)
@@ -42,6 +42,7 @@ centroid varying vec2 varTexCoord;
        varying float perspective_factor;
 #endif
 
+varying float area_enable_parallax;
 
 varying vec3 eyeVec;
 varying float nightRatio;
@@ -193,6 +194,9 @@ void main(void)
 
        vPosition = gl_Position.xyz;
        eyeVec = -(mWorldView * pos).xyz;
+#ifdef SECONDSTAGE
+       normalPass = normalize((inVertexNormal+1)/2);
+#endif
        vNormal = inVertexNormal;
 
        // Calculate color.
index 1fefc764b37bff4c741de7f49270b81f6ce59919..662d0c8659b43a4c440935fa6c9788fb0c3064da 100644 (file)
@@ -361,39 +361,6 @@ float getShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance)
 #endif
 #endif
 
-#if ENABLE_TONE_MAPPING
-
-/* Hable's UC2 Tone mapping parameters
-       A = 0.22;
-       B = 0.30;
-       C = 0.10;
-       D = 0.20;
-       E = 0.01;
-       F = 0.30;
-       W = 11.2;
-       equation used:  ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
-*/
-
-vec3 uncharted2Tonemap(vec3 x)
-{
-       return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
-}
-
-vec4 applyToneMapping(vec4 color)
-{
-       color = vec4(pow(color.rgb, vec3(2.2)), color.a);
-       const float gamma = 1.6;
-       const float exposureBias = 5.5;
-       color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
-       // Precalculated white_scale from
-       //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
-       vec3 whiteScale = vec3(1.036015346);
-       color.rgb *= whiteScale;
-       return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
-}
-#endif
-
-
 
 void main(void)
 {
@@ -473,10 +440,6 @@ void main(void)
        }
 #endif
 
-#if ENABLE_TONE_MAPPING
-       col = applyToneMapping(col);
-#endif
-
        // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?),
        // the fog will only be rendered correctly if the last operation before the
        // clamp() is an addition. Else, the clamp() seems to be ignored.
@@ -491,5 +454,5 @@ void main(void)
        col = mix(skyBgColor, col, clarity);
        col = vec4(col.rgb, base.a);
 
-       gl_FragColor = col;
+       gl_FragData[0] = col;
 }
diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl
new file mode 100644 (file)
index 0000000..965450f
--- /dev/null
@@ -0,0 +1,53 @@
+uniform sampler2D baseTexture;
+
+#define rendered baseTexture
+
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+#if ENABLE_TONE_MAPPING
+
+/* Hable's UC2 Tone mapping parameters
+       A = 0.22;
+       B = 0.30;
+       C = 0.10;
+       D = 0.20;
+       E = 0.01;
+       F = 0.30;
+       W = 11.2;
+       equation used:  ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F
+*/
+
+vec3 uncharted2Tonemap(vec3 x)
+{
+       return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333;
+}
+
+vec4 applyToneMapping(vec4 color)
+{
+       color = vec4(pow(color.rgb, vec3(2.2)), color.a);
+       const float gamma = 1.6;
+       const float exposureBias = 5.5;
+       color.rgb = uncharted2Tonemap(exposureBias * color.rgb);
+       // Precalculated white_scale from
+       //vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
+       vec3 whiteScale = vec3(1.036015346);
+       color.rgb *= whiteScale;
+       return vec4(pow(color.rgb, vec3(1.0 / gamma)), color.a);
+}
+#endif
+
+void main(void)
+{
+       vec2 uv = varTexCoord.st;
+       vec4 color = texture2D(rendered, uv).rgba;
+
+#if ENABLE_TONE_MAPPING
+       color = applyToneMapping(color);
+#endif
+
+       gl_FragColor = vec4(color.rgb, 1.0); // force full alpha to avoid holes in the image.
+}
diff --git a/client/shaders/second_stage/opengl_vertex.glsl b/client/shaders/second_stage/opengl_vertex.glsl
new file mode 100644 (file)
index 0000000..12692c2
--- /dev/null
@@ -0,0 +1,11 @@
+#ifdef GL_ES
+varying mediump vec2 varTexCoord;
+#else
+centroid varying vec2 varTexCoord;
+#endif
+
+void main(void)
+{
+       varTexCoord.st = inTexCoord0.st;
+       gl_Position = inVertexPosition;
+}
index 35b1f8902dc06aa173e5df1dd129098f63fb6f5f..2094ea0f4b38c5614a8e23d9cffb5d6a5bb05aeb 100644 (file)
@@ -8,5 +8,5 @@ void main(void)
        vec2 uv = varTexCoord.st;
        vec4 color = texture2D(baseTexture, uv);
        color.rgb *= varColor.rgb;
-       gl_FragColor = color;
+       gl_FragData[0] = color;
 }
index 656ad45cebf01a773afaea4943b00571cb8eb2b0..7aa43d19884a05eebe7d0f07603682894ae57403 100644 (file)
@@ -21,10 +21,11 @@ set(client_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/render/core.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/render/factory.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/render/interlaced.cpp
-       ${CMAKE_CURRENT_SOURCE_DIR}/render/pageflip.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/render/plain.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/render/sidebyside.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/render/stereo.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/render/secondstage.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/render/pipeline.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/camera.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/client.cpp
index df75c52d63488a47780958862797c890b7067e93..fd60e6dde494b4eb4ddcc68323f27b17d6a22b18 100644 (file)
@@ -627,14 +627,11 @@ void Camera::wield(const ItemStack &item)
 
 void Camera::drawWieldedTool(irr::core::matrix4* translation)
 {
-       // Clear Z buffer so that the wielded tool stays in front of world geometry
-       m_wieldmgr->getVideoDriver()->clearBuffers(video::ECBF_DEPTH);
-
        // Draw the wielded node (in a separate scene manager)
        scene::ICameraSceneNode* cam = m_wieldmgr->getActiveCamera();
        cam->setAspectRatio(m_cameranode->getAspectRatio());
        cam->setFOV(72.0*M_PI/180.0);
-       cam->setNearValue(10);
+       cam->setNearValue(40); // give wield tool smaller z-depth than the world in most cases.
        cam->setFarValue(1000);
        if (translation != NULL)
        {
index c0c28960855f4f212fb3103cfaeb8ff2c6fae1af..3455908ce4d1ae382d85d9acc0d4df45584eab5e 100644 (file)
@@ -150,7 +150,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
                bool selected)
 {
        if (selected) {
-               /* draw hihlighting around selected item */
+               /* draw highlighting around selected item */
                if (use_hotbar_selected_image) {
                        core::rect<s32> imgrect2 = rect;
                        imgrect2.UpperLeftCorner.X  -= (m_padding*2);
index 2571f7333bfd1458b71ddaa64ea37da5327d2945..46f417900e6c213b425b46f92e91cd1a1a0791a3 100644 (file)
@@ -19,17 +19,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "anaglyph.h"
+#include "client/camera.h"
 
-void RenderingCoreAnaglyph::drawAll()
-{
-       renderBothImages();
-       drawPostFx();
-       drawHUD();
-}
 
-void RenderingCoreAnaglyph::setupMaterial(int color_mask)
+/// SetColorMaskStep step
+
+SetColorMaskStep::SetColorMaskStep(int _color_mask)
+       : color_mask(_color_mask)
+{}
+
+void SetColorMaskStep::run(PipelineContext &context)
 {
-       video::SOverrideMaterial &mat = driver->getOverrideMaterial();
+       video::SOverrideMaterial &mat = context.device->getVideoDriver()->getOverrideMaterial();
        mat.reset();
        mat.Material.ColorMask = color_mask;
        mat.EnableFlags = video::EMF_COLOR_MASK;
@@ -38,15 +39,53 @@ void RenderingCoreAnaglyph::setupMaterial(int color_mask)
                           scene::ESNRP_SHADOW;
 }
 
-void RenderingCoreAnaglyph::useEye(bool right)
+/// ClearDepthBufferTarget
+
+ClearDepthBufferTarget::ClearDepthBufferTarget(RenderTarget *_target) :
+       target(_target)
+{}
+
+void ClearDepthBufferTarget::activate(PipelineContext &context)
+{
+       target->activate(context);
+       context.device->getVideoDriver()->clearBuffers(video::ECBF_DEPTH);
+}
+
+ConfigureOverrideMaterialTarget::ConfigureOverrideMaterialTarget(RenderTarget *_upstream, bool _enable) :
+       upstream(_upstream), enable(_enable)
+{
+}
+
+void ConfigureOverrideMaterialTarget::activate(PipelineContext &context)
 {
-       RenderingCoreStereo::useEye(right);
-       driver->clearBuffers(video::ECBF_DEPTH);
-       setupMaterial(right ? video::ECP_GREEN | video::ECP_BLUE : video::ECP_RED);
+       upstream->activate(context);
+       context.device->getVideoDriver()->getOverrideMaterial().Enabled = enable;
 }
 
-void RenderingCoreAnaglyph::resetEye()
+
+void populateAnaglyphPipeline(RenderPipeline *pipeline, Client *client)
 {
-       setupMaterial(video::ECP_ALL);
-       RenderingCoreStereo::resetEye();
+       // clear depth buffer every time 3D is rendered
+       auto step3D = pipeline->own(create3DStage(client, v2f(1.0)));
+       auto screen = pipeline->createOwned<ScreenTarget>();
+       auto clear_depth = pipeline->createOwned<ClearDepthBufferTarget>(screen);
+       auto enable_override_material = pipeline->createOwned<ConfigureOverrideMaterialTarget>(clear_depth, true);
+       step3D->setRenderTarget(enable_override_material);
+
+       // left eye
+       pipeline->addStep(pipeline->createOwned<OffsetCameraStep>(false));
+       pipeline->addStep(pipeline->createOwned<SetColorMaskStep>(video::ECP_RED));
+       pipeline->addStep(step3D);
+
+       // right eye
+       pipeline->addStep(pipeline->createOwned<OffsetCameraStep>(true));
+       pipeline->addStep(pipeline->createOwned<SetColorMaskStep>(video::ECP_GREEN | video::ECP_BLUE));
+       pipeline->addStep(step3D);
+
+       // reset
+       pipeline->addStep(pipeline->createOwned<OffsetCameraStep>(0.0f));
+       pipeline->addStep(pipeline->createOwned<SetColorMaskStep>(video::ECP_ALL));
+       
+       pipeline->addStep(pipeline->createOwned<MapPostFxStep>());
+       pipeline->addStep(pipeline->createOwned<DrawHUD>());
 }
index a03b7dc7e7e516f8eb6a1876b0df1c337463793c..7a186aeffad81c945cb712b75eae72d81f8e7fb9 100644 (file)
@@ -20,15 +20,50 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 #include "stereo.h"
+#include "pipeline.h"
 
-class RenderingCoreAnaglyph : public RenderingCoreStereo
+/**
+ * Set color mask when rendering the next steps
+ */
+class SetColorMaskStep : public TrivialRenderStep
 {
-protected:
-       void setupMaterial(int color_mask);
-       void useEye(bool right) override;
-       void resetEye() override;
+public:
+       SetColorMaskStep(int color_mask);
+
+       void run(PipelineContext &context) override;
+private:
+       int color_mask;
+};
+
+/**
+ * Resets depth buffer of the current render target
+ * 
+ */
+class ClearDepthBufferTarget : public RenderTarget
+{
+public:
+       ClearDepthBufferTarget(RenderTarget *target);
 
+       void reset(PipelineContext &context) override {}
+       void activate(PipelineContext &context) override;
+private:
+       RenderTarget *target;
+};
+
+
+/**
+ * Enables or disables override material when activated
+ * 
+ */
+class ConfigureOverrideMaterialTarget : public RenderTarget
+{
 public:
-       using RenderingCoreStereo::RenderingCoreStereo;
-       void drawAll() override;
+       ConfigureOverrideMaterialTarget(RenderTarget *upstream, bool enable);
+
+       virtual void activate(PipelineContext &context) override;
+private:
+       RenderTarget *upstream;
+       bool enable;
 };
+
+void populateAnaglyphPipeline(RenderPipeline *pipeline, Client *client);
index ca5d3c614622e9e0014502aa9e41259abf462b5a..622ea1f9f4a52c0f38a38bfe742ecdf7a6b20139 100644 (file)
@@ -19,111 +19,48 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "core.h"
-#include "client/camera.h"
-#include "client/client.h"
-#include "client/clientmap.h"
-#include "client/hud.h"
-#include "client/minimap.h"
+#include "plain.h"
 #include "client/shadows/dynamicshadowsrender.h"
+#include "settings.h"
 
-RenderingCore::RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud)
-       : device(_device), driver(device->getVideoDriver()), smgr(device->getSceneManager()),
-       guienv(device->getGUIEnvironment()), client(_client), camera(client->getCamera()),
-       mapper(client->getMinimap()), hud(_hud),
-       shadow_renderer(nullptr)
+RenderingCore::RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud, 
+               ShadowRenderer *_shadow_renderer, RenderPipeline *_pipeline, v2f _virtual_size_scale)
+       : device(_device), client(_client), hud(_hud), shadow_renderer(_shadow_renderer), 
+       pipeline(_pipeline), virtual_size_scale(_virtual_size_scale)
 {
-       screensize = driver->getScreenSize();
-       virtual_size = screensize;
-
-       // disable if unsupported
-       if (g_settings->getBool("enable_dynamic_shadows") && (
-               g_settings->get("video_driver") != "opengl" ||
-               !g_settings->getBool("enable_shaders"))) {
-               g_settings->setBool("enable_dynamic_shadows", false);
-       }
-
-       if (g_settings->getBool("enable_shaders") &&
-                       g_settings->getBool("enable_dynamic_shadows")) {
-               shadow_renderer = new ShadowRenderer(device, client);
-       }
 }
 
 RenderingCore::~RenderingCore()
 {
-       clearTextures();
+       delete pipeline;
        delete shadow_renderer;
 }
 
 void RenderingCore::initialize()
 {
-       // have to be called late as the VMT is not ready in the constructor:
-       initTextures();
        if (shadow_renderer)
-               shadow_renderer->initialize();
-}
+               pipeline->addStep<RenderShadowMapStep>();
 
-void RenderingCore::updateScreenSize()
-{
-       virtual_size = screensize;
-       clearTextures();
-       initTextures();
+       createPipeline();
 }
 
 void RenderingCore::draw(video::SColor _skycolor, bool _show_hud, bool _show_minimap,
                bool _draw_wield_tool, bool _draw_crosshair)
 {
-       v2u32 ss = driver->getScreenSize();
-       if (screensize != ss) {
-               screensize = ss;
-               updateScreenSize();
-       }
-       skycolor = _skycolor;
-       show_hud = _show_hud;
-       show_minimap = _show_minimap;
-       draw_wield_tool = _draw_wield_tool;
-       draw_crosshair = _draw_crosshair;
-
-       if (shadow_renderer) {
-               // This is necessary to render shadows for animations correctly
-               smgr->getRootSceneNode()->OnAnimate(device->getTimer()->getTime());
-               shadow_renderer->update();
-       }
-
-       beforeDraw();
-       drawAll();
-}
-
-void RenderingCore::draw3D()
-{
-       smgr->drawAll();
-       if (shadow_renderer)
-               shadow_renderer->drawDebug();
+       v2u32 screensize = device->getVideoDriver()->getScreenSize();
+       virtual_size = v2u32(screensize.X * virtual_size_scale.X, screensize.Y * virtual_size_scale.Y);
 
-       driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
-       if (!show_hud)
-               return;
-       hud->drawBlockBounds();
-       hud->drawSelectionMesh();
-       if (draw_wield_tool)
-               camera->drawWieldedTool();
-}
+       PipelineContext context(device, client, hud, shadow_renderer, _skycolor, screensize);
+       context.draw_crosshair = _draw_crosshair;
+       context.draw_wield_tool = _draw_wield_tool;
+       context.show_hud = _show_hud;
+       context.show_minimap = _show_minimap;
 
-void RenderingCore::drawHUD()
-{
-       if (show_hud) {
-               if (draw_crosshair)
-                       hud->drawCrosshair();
-
-               hud->drawHotbar(client->getEnv().getLocalPlayer()->getWieldIndex());
-               hud->drawLuaElements(camera->getOffset());
-               camera->drawNametags();
-               if (mapper && show_minimap)
-                       mapper->drawMinimap();
-       }
-       guienv->drawAll();
+       pipeline->reset(context);
+       pipeline->run(context);
 }
 
-void RenderingCore::drawPostFx()
+v2u32 RenderingCore::getVirtualSize() const
 {
-       client->getEnv().getClientMap().renderPostFx(camera->getCameraMode());
-}
+       return virtual_size;
+}
\ No newline at end of file
index cabfbbfad95181f3b7fffbff2cabcd214ab5d0af..7b882436df8ed2301a91a3e14b29c1b2cdd68629 100644 (file)
@@ -26,43 +26,28 @@ class Camera;
 class Client;
 class Hud;
 class Minimap;
+class RenderPipeline;
+class RenderTarget;
 
 class RenderingCore
 {
 protected:
-       v2u32 screensize;
-       v2u32 virtual_size;
-       video::SColor skycolor;
-       bool show_hud;
-       bool show_minimap;
-       bool draw_wield_tool;
-       bool draw_crosshair;
-
        IrrlichtDevice *device;
-       video::IVideoDriver *driver;
-       scene::ISceneManager *smgr;
-       gui::IGUIEnvironment *guienv;
-
        Client *client;
-       Camera *camera;
-       Minimap *mapper;
        Hud *hud;
-
        ShadowRenderer *shadow_renderer;
 
-       void updateScreenSize();
-       virtual void initTextures() {}
-       virtual void clearTextures() {}
+       RenderPipeline *pipeline;
 
-       virtual void beforeDraw() {}
-       virtual void drawAll() = 0;
+       v2f virtual_size_scale;
+       v2u32 virtual_size { 0, 0 };
 
-       void draw3D();
-       void drawHUD();
-       void drawPostFx();
+       virtual void createPipeline() {}
 
 public:
-       RenderingCore(IrrlichtDevice *_device, Client *_client, Hud *_hud);
+       RenderingCore(IrrlichtDevice *device, Client *client, Hud *hud, 
+                       ShadowRenderer *shadow_renderer, RenderPipeline *pipeline,
+                       v2f virtual_size_scale);
        RenderingCore(const RenderingCore &) = delete;
        RenderingCore(RenderingCore &&) = delete;
        virtual ~RenderingCore();
@@ -74,7 +59,7 @@ class RenderingCore
        void draw(video::SColor _skycolor, bool _show_hud, bool _show_minimap,
                        bool _draw_wield_tool, bool _draw_crosshair);
 
-       inline v2u32 getVirtualSize() const { return virtual_size; }
+       v2u32 getVirtualSize() const;
 
        ShadowRenderer *get_shadow_renderer() { return shadow_renderer; };
 };
index 7fcec40dd32f1965c1a5d9a5e450bed74089f20b..4992e61928dd0c14f5a271f21e3f3f73ec0787a3 100644 (file)
@@ -23,30 +23,63 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "plain.h"
 #include "anaglyph.h"
 #include "interlaced.h"
-#include "pageflip.h"
 #include "sidebyside.h"
+#include "secondstage.h"
+#include "client/shadows/dynamicshadowsrender.h"
+
+struct CreatePipelineResult
+{
+       v2f virtual_size_scale;
+       ShadowRenderer *shadow_renderer { nullptr };
+       RenderPipeline *pipeline { nullptr };
+};
+
+void createPipeline(const std::string &stereo_mode, IrrlichtDevice *device, Client *client, Hud *hud, CreatePipelineResult &result);
 
 RenderingCore *createRenderingCore(const std::string &stereo_mode, IrrlichtDevice *device,
                Client *client, Hud *hud)
 {
-       if (stereo_mode == "none")
-               return new RenderingCorePlain(device, client, hud);
-       if (stereo_mode == "anaglyph")
-               return new RenderingCoreAnaglyph(device, client, hud);
-       if (stereo_mode == "interlaced")
-               return new RenderingCoreInterlaced(device, client, hud);
-#ifdef STEREO_PAGEFLIP_SUPPORTED
-       if (stereo_mode == "pageflip")
-               return new RenderingCorePageflip(device, client, hud);
-#endif
-       if (stereo_mode == "sidebyside")
-               return new RenderingCoreSideBySide(device, client, hud);
-       if (stereo_mode == "topbottom")
-               return new RenderingCoreSideBySide(device, client, hud, true);
-       if (stereo_mode == "crossview")
-               return new RenderingCoreSideBySide(device, client, hud, false, true);
+       CreatePipelineResult created_pipeline;
+       createPipeline(stereo_mode, device, client, hud, created_pipeline);
+       return new RenderingCore(device, client, hud, 
+                       created_pipeline.shadow_renderer, created_pipeline.pipeline, created_pipeline.virtual_size_scale);
+}
+
+void createPipeline(const std::string &stereo_mode, IrrlichtDevice *device, Client *client, Hud *hud, CreatePipelineResult &result)
+{
+       result.shadow_renderer = createShadowRenderer(device, client);
+       result.virtual_size_scale = v2f(1.0f);
+       result.pipeline = new RenderPipeline();
+
+       if (result.shadow_renderer)
+               result.pipeline->addStep<RenderShadowMapStep>();
+
+       if (stereo_mode == "none") {
+               populatePlainPipeline(result.pipeline, client);
+               return;
+       }
+       if (stereo_mode == "anaglyph") {
+               populateAnaglyphPipeline(result.pipeline, client);
+               return;
+       }
+       if (stereo_mode == "interlaced") {
+               populateInterlacedPipeline(result.pipeline, client);
+               return;
+       }
+       if (stereo_mode == "sidebyside") {
+               populateSideBySidePipeline(result.pipeline, client, false, false, result.virtual_size_scale);
+               return;
+       }
+       if (stereo_mode == "topbottom") {
+               populateSideBySidePipeline(result.pipeline, client, true, false, result.virtual_size_scale);
+               return;
+       }
+       if (stereo_mode == "crossview") {
+               populateSideBySidePipeline(result.pipeline, client, false, true, result.virtual_size_scale);
+               return;
+       }
 
        // fallback to plain renderer
        errorstream << "Invalid rendering mode: " << stereo_mode << std::endl;
-       return new RenderingCorePlain(device, client, hud);
-}
+       populatePlainPipeline(result.pipeline, client);
+}
\ No newline at end of file
index 3f79a8eb57909bff21a34058b765ad1ef7fd9728..f4accd39bd6091f0a61a44fbea990965ddd3fbc0 100644 (file)
@@ -19,102 +19,66 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "interlaced.h"
+#include "secondstage.h"
 #include "client/client.h"
 #include "client/shader.h"
-#include "client/tile.h"
+#include "client/camera.h"
 
-RenderingCoreInterlaced::RenderingCoreInterlaced(
-       IrrlichtDevice *_device, Client *_client, Hud *_hud)
-       : RenderingCoreStereo(_device, _client, _hud)
+InitInterlacedMaskStep::InitInterlacedMaskStep(TextureBuffer *_buffer, u8 _index)      : 
+       buffer(_buffer), index(_index)
 {
-       initMaterial();
 }
 
-void RenderingCoreInterlaced::initMaterial()
+void InitInterlacedMaskStep::run(PipelineContext &context)
 {
-       IShaderSource *s = client->getShaderSource();
-       mat.UseMipMaps = false;
-       mat.ZBuffer = false;
-#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8
-       mat.ZWriteEnable = video::EZW_OFF;
-#else
-       mat.ZWriteEnable = false;
-#endif
-       u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC);
-       mat.MaterialType = s->getShaderInfo(shader).material;
-       for (int k = 0; k < 3; ++k) {
-               mat.TextureLayer[k].AnisotropicFilter = false;
-               mat.TextureLayer[k].BilinearFilter = false;
-               mat.TextureLayer[k].TrilinearFilter = false;
-               mat.TextureLayer[k].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
-               mat.TextureLayer[k].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
-       }
-}
-
-void RenderingCoreInterlaced::initTextures()
-{
-       v2u32 image_size{screensize.X, screensize.Y / 2};
-       left = driver->addRenderTargetTexture(
-                       image_size, "3d_render_left", video::ECF_A8R8G8B8);
-       right = driver->addRenderTargetTexture(
-                       image_size, "3d_render_right", video::ECF_A8R8G8B8);
-       mask = driver->addTexture(screensize, "3d_render_mask", video::ECF_A8R8G8B8);
-       initMask();
-       mat.TextureLayer[0].Texture = left;
-       mat.TextureLayer[1].Texture = right;
-       mat.TextureLayer[2].Texture = mask;
-}
-
-void RenderingCoreInterlaced::clearTextures()
-{
-       driver->removeTexture(left);
-       driver->removeTexture(right);
-       driver->removeTexture(mask);
-}
+       video::ITexture *mask = buffer->getTexture(index);
+       if (!mask)
+               return;
+       if (mask == last_mask)
+               return;
+       last_mask = mask;
 
-void RenderingCoreInterlaced::initMask()
-{
+       auto size = mask->getSize();
        u8 *data = reinterpret_cast<u8 *>(mask->lock());
-       for (u32 j = 0; j < screensize.Y; j++) {
+       for (u32 j = 0; j < size.Height; j++) {
                u8 val = j % 2 ? 0xff : 0x00;
-               memset(data, val, 4 * screensize.X);
-               data += 4 * screensize.X;
+               memset(data, val, 4 * size.Width);
+               data += 4 * size.Width;
        }
        mask->unlock();
 }
 
-void RenderingCoreInterlaced::drawAll()
+void populateInterlacedPipeline(RenderPipeline *pipeline, Client *client)
 {
-       renderBothImages();
-       merge();
-       drawHUD();
-}
+       static const u8 TEXTURE_LEFT = 0;
+       static const u8 TEXTURE_RIGHT = 1;
+       static const u8 TEXTURE_MASK = 2;
 
-void RenderingCoreInterlaced::merge()
-{
-       static const video::S3DVertex vertices[4] = {
-                       video::S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
-                                       video::SColor(255, 0, 255, 255), 1.0, 0.0),
-                       video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
-                                       video::SColor(255, 255, 0, 255), 0.0, 0.0),
-                       video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
-                                       video::SColor(255, 255, 255, 0), 0.0, 1.0),
-                       video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
-                                       video::SColor(255, 255, 255, 255), 1.0, 1.0),
-       };
-       static const u16 indices[6] = {0, 1, 2, 2, 3, 0};
-       driver->setMaterial(mat);
-       driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2);
-}
+       TextureBuffer *buffer = pipeline->createOwned<TextureBuffer>();
+       buffer->setTexture(TEXTURE_LEFT, v2f(1.0f, 0.5f), "3d_render_left", video::ECF_A8R8G8B8);
+       buffer->setTexture(TEXTURE_RIGHT, v2f(1.0f, 0.5f), "3d_render_right", video::ECF_A8R8G8B8);
+       buffer->setTexture(TEXTURE_MASK, v2f(1.0f, 1.0f), "3d_render_mask", video::ECF_A8R8G8B8);
 
-void RenderingCoreInterlaced::useEye(bool _right)
-{
-       driver->setRenderTarget(_right ? right : left, true, true, skycolor);
-       RenderingCoreStereo::useEye(_right);
-}
+       pipeline->addStep<InitInterlacedMaskStep>(buffer, TEXTURE_MASK);
 
-void RenderingCoreInterlaced::resetEye()
-{
-       driver->setRenderTarget(nullptr, false, false, skycolor);
-       RenderingCoreStereo::resetEye();
-}
+       auto step3D = pipeline->own(create3DStage(client, v2f(1.0f, 0.5f)));
+
+       // eyes
+       for (bool right : { false, true }) {
+               pipeline->addStep<OffsetCameraStep>(right);
+               auto output = pipeline->createOwned<TextureBufferOutput>(buffer, right ? TEXTURE_RIGHT : TEXTURE_LEFT);
+               pipeline->addStep<SetRenderTargetStep>(step3D, output);
+               pipeline->addStep(step3D);
+               pipeline->addStep<MapPostFxStep>();
+       }
+
+       pipeline->addStep<OffsetCameraStep>(0.0f);
+       IShaderSource *s = client->getShaderSource();
+       u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC);
+       video::E_MATERIAL_TYPE material = s->getShaderInfo(shader).material;
+       auto texture_map = { TEXTURE_LEFT, TEXTURE_RIGHT, TEXTURE_MASK };
+       auto merge = pipeline->addStep<PostProcessingStep>(material, texture_map);
+       merge->setRenderSource(buffer);
+       merge->setRenderTarget(pipeline->createOwned<ScreenTarget>());
+       pipeline->addStep<DrawHUD>();
+}
\ No newline at end of file
index 71815fd707d7297d30dd40553219b279b624fe9d..b22fbdac61f66db49ad1c5fb292c5a7b1e3d6fea 100644 (file)
@@ -21,23 +21,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #pragma once
 #include "stereo.h"
 
-class RenderingCoreInterlaced : public RenderingCoreStereo
+class InitInterlacedMaskStep : public TrivialRenderStep
 {
-protected:
-       video::ITexture *left = nullptr;
-       video::ITexture *right = nullptr;
-       video::ITexture *mask = nullptr;
-       video::SMaterial mat;
-
-       void initMaterial();
-       void initTextures() override;
-       void clearTextures() override;
-       void initMask();
-       void useEye(bool right) override;
-       void resetEye() override;
-       void merge();
-
 public:
-       RenderingCoreInterlaced(IrrlichtDevice *_device, Client *_client, Hud *_hud);
-       void drawAll() override;
+       InitInterlacedMaskStep(TextureBuffer *buffer, u8 index);
+       void run(PipelineContext &context);
+private:
+       TextureBuffer *buffer;
+       video::ITexture *last_mask { nullptr };
+       u8 index;
 };
+
+void populateInterlacedPipeline(RenderPipeline *pipeline, Client *client);
diff --git a/src/client/render/pageflip.cpp b/src/client/render/pageflip.cpp
deleted file mode 100644 (file)
index f3a2190..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2017 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 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, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "pageflip.h"
-
-#ifdef STEREO_PAGEFLIP_SUPPORTED
-
-void RenderingCorePageflip::initTextures()
-{
-       hud = driver->addRenderTargetTexture(
-                       screensize, "3d_render_hud", video::ECF_A8R8G8B8);
-}
-
-void RenderingCorePageflip::clearTextures()
-{
-       driver->removeTexture(hud);
-}
-
-void RenderingCorePageflip::drawAll()
-{
-       driver->setRenderTarget(hud, true, true, video::SColor(0, 0, 0, 0));
-       drawHUD();
-       driver->setRenderTarget(nullptr, false, false, skycolor);
-       renderBothImages();
-}
-
-void RenderingCorePageflip::useEye(bool _right)
-{
-       driver->setRenderTarget(_right ? video::ERT_STEREO_RIGHT_BUFFER
-                                      : video::ERT_STEREO_LEFT_BUFFER,
-                       true, true, skycolor);
-       RenderingCoreStereo::useEye(_right);
-}
-
-void RenderingCorePageflip::resetEye()
-{
-       driver->draw2DImage(hud, v2s32(0, 0));
-       driver->setRenderTarget(video::ERT_FRAME_BUFFER, false, false, skycolor);
-       RenderingCoreStereo::resetEye();
-}
-
-#endif // STEREO_PAGEFLIP_SUPPORTED
diff --git a/src/client/render/pageflip.h b/src/client/render/pageflip.h
deleted file mode 100644 (file)
index 17711b0..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2017 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 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, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#pragma once
-#include "stereo.h"
-
-// The support is absent in 1.9.0 (dropped in r5068)
-#if (IRRLICHT_VERSION_MAJOR == 1) && (IRRLICHT_VERSION_MINOR <= 8)
-#define STEREO_PAGEFLIP_SUPPORTED
-
-class RenderingCorePageflip : public RenderingCoreStereo
-{
-protected:
-       video::ITexture *hud = nullptr;
-
-       void initTextures() override;
-       void clearTextures() override;
-       void useEye(bool right) override;
-       void resetEye() override;
-
-public:
-       using RenderingCoreStereo::RenderingCoreStereo;
-       void drawAll() override;
-};
-
-#endif
diff --git a/src/client/render/pipeline.cpp b/src/client/render/pipeline.cpp
new file mode 100644 (file)
index 0000000..baf215d
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+Minetest
+Copyright (C) 2022 x2048, Dmitry Kostenko <codeforsmile@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 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, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "pipeline.h"
+#include "client/client.h"
+#include "client/hud.h"
+
+#include <vector>
+#include <memory>
+
+
+TextureBuffer::~TextureBuffer()
+{
+       if (m_render_target)
+               m_driver->removeRenderTarget(m_render_target);
+       m_render_target = nullptr;
+       for (u32 index = 0; index < m_textures.size(); index++)
+               m_driver->removeTexture(m_textures[index]);
+       m_textures.clear();
+}
+
+video::ITexture *TextureBuffer::getTexture(u8 index)
+{
+       if (index == m_depth_texture_index)
+               return m_depth_texture;
+       if (index >= m_textures.size())
+               return nullptr;
+       return m_textures[index];
+}
+
+
+void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format)
+{
+       assert(index != NO_DEPTH_TEXTURE);
+
+       if (m_definitions.size() <= index)
+               m_definitions.resize(index + 1);
+
+       if (m_depth_texture_index == index)
+               m_depth_texture_index = NO_DEPTH_TEXTURE;
+       
+       auto &definition = m_definitions[index];
+       definition.valid = true;
+       definition.dirty = true;
+       definition.fixed_size = true;
+       definition.size = size;
+       definition.name = name;
+       definition.format = format;
+}
+
+void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format)
+{
+       assert(index != NO_DEPTH_TEXTURE);
+
+       if (m_definitions.size() <= index)
+               m_definitions.resize(index + 1);
+       
+       if (m_depth_texture_index == index)
+               m_depth_texture_index = NO_DEPTH_TEXTURE;
+
+       auto &definition = m_definitions[index];
+       definition.valid = true;
+       definition.dirty = true;
+       definition.fixed_size = false;
+       definition.scale_factor = scale_factor;
+       definition.name = name;
+       definition.format = format;
+}
+
+void TextureBuffer::setDepthTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format)
+{
+       assert(index != NO_DEPTH_TEXTURE);
+       setTexture(index, size, name, format);
+       m_depth_texture_index = index;
+}
+
+void TextureBuffer::setDepthTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format)
+{
+       assert(index != NO_DEPTH_TEXTURE);
+       setTexture(index, scale_factor, name, format);
+       m_depth_texture_index = index;
+}
+
+void TextureBuffer::reset(PipelineContext &context)
+{
+       if (!m_driver)
+               m_driver = context.device->getVideoDriver();
+
+       // remove extra textures
+       if (m_textures.size() > m_definitions.size()) {
+               for (unsigned i = m_definitions.size(); i < m_textures.size(); i++)
+                       if (m_textures[i])
+                               m_driver->removeTexture(m_textures[i]);
+
+               m_textures.set_used(m_definitions.size());
+       }
+
+       // add placeholders for new definitions
+       while (m_textures.size() < m_definitions.size())
+               m_textures.push_back(nullptr);
+
+       // change textures to match definitions
+       bool modified = false;
+       for (u32 i = 0; i < m_definitions.size(); i++) {
+               video::ITexture **ptr = &m_textures[i];
+               if (i == m_depth_texture_index) {
+                       if (*ptr) {
+                               m_driver->removeTexture(*ptr);
+                               *ptr = nullptr;
+                       }
+                       ptr = &m_depth_texture;
+               }
+
+               if (ensureTexture(ptr, m_definitions[i], context))
+                       modified = true;
+               m_definitions[i].dirty = false;
+       }
+       
+       // make sude depth texture is removed and reset
+       if (m_depth_texture_index == NO_DEPTH_TEXTURE && m_depth_texture) {
+               m_driver->removeTexture(m_depth_texture);
+               m_depth_texture = nullptr;
+       }
+
+       if (!m_render_target)
+               m_render_target = m_driver->addRenderTarget();
+
+       if (modified)
+               m_render_target->setTexture(m_textures, m_depth_texture);
+
+       RenderTarget::reset(context);
+}
+
+void TextureBuffer::activate(PipelineContext &context)
+{
+       m_driver->setRenderTargetEx(m_render_target, m_clear ? video::ECBF_DEPTH | video::ECBF_COLOR : 0, context.clear_color);
+       RenderTarget::activate(context);
+}
+
+bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefinition& definition, PipelineContext &context)
+{
+       bool modify;
+       core::dimension2du size;
+       if (definition.valid) {
+               if (definition.fixed_size)
+                       size = definition.size;
+               else
+                       size = core::dimension2du(
+                                       (u32)(context.target_size.X * definition.scale_factor.X),
+                                       (u32)(context.target_size.Y * definition.scale_factor.Y));
+               
+               modify = definition.dirty || (*texture == nullptr) || (*texture)->getSize() != size;
+       }
+       else {
+               modify = (*texture != nullptr);
+       }
+
+       if (!modify)
+               return false;
+
+       if (*texture)
+               m_driver->removeTexture(*texture);
+
+       if (definition.valid)
+               *texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format);
+       else
+               *texture = nullptr;
+
+       return true;
+}
+
+TextureBufferOutput::TextureBufferOutput(TextureBuffer *_buffer, u8 _texture_index)
+       : buffer(_buffer), texture_index(_texture_index)
+{}
+
+void TextureBufferOutput::activate(PipelineContext &context)
+{
+       auto texture = buffer->getTexture(texture_index);
+       auto driver = context.device->getVideoDriver();
+       driver->setRenderTarget(texture, m_clear, m_clear, context.clear_color);
+       driver->OnResize(texture->getSize());
+
+       RenderTarget::activate(context);
+}
+
+u8 DynamicSource::getTextureCount()
+{
+       assert(isConfigured());
+       return upstream->getTextureCount();
+}
+
+video::ITexture *DynamicSource::getTexture(u8 index)
+{
+       assert(isConfigured());
+       return upstream->getTexture(index);
+}
+
+void ScreenTarget::activate(PipelineContext &context)
+{
+       auto driver = context.device->getVideoDriver();
+       driver->setRenderTarget(nullptr, m_clear, m_clear, context.clear_color);
+       driver->OnResize(size);
+       RenderTarget::activate(context);
+}
+
+void DynamicTarget::activate(PipelineContext &context)
+{
+       if (!isConfigured())
+               throw std::logic_error("Dynamic render target is not configured before activation.");
+       upstream->activate(context);
+}
+
+void ScreenTarget::reset(PipelineContext &context)
+{
+       RenderTarget::reset(context);
+       size = context.device->getVideoDriver()->getScreenSize();
+}
+
+SetRenderTargetStep::SetRenderTargetStep(RenderStep *_step, RenderTarget *_target)
+       : step(_step), target(_target)
+{
+}
+
+void SetRenderTargetStep::run(PipelineContext &context)
+{
+       step->setRenderTarget(target);
+}
+
+RenderSource *RenderPipeline::getInput()
+{
+       return &m_input;
+}
+
+RenderTarget *RenderPipeline::getOutput()
+{
+       return &m_output;
+}
+
+void RenderPipeline::run(PipelineContext &context)
+{
+       v2u32 original_size = context.target_size;
+       context.target_size = v2u32(original_size.X * scale.X, original_size.Y * scale.Y);
+
+       for (auto &object : m_objects)
+               object->reset(context);
+
+       for (auto &step: m_pipeline)
+               step->run(context);
+       
+       context.target_size = original_size;
+}
+
+void RenderPipeline::setRenderSource(RenderSource *source)
+{
+       m_input.setRenderSource(source);
+}
+
+void RenderPipeline::setRenderTarget(RenderTarget *target)
+{
+       m_output.setRenderTarget(target);
+}
diff --git a/src/client/render/pipeline.h b/src/client/render/pipeline.h
new file mode 100644 (file)
index 0000000..4a6df7d
--- /dev/null
@@ -0,0 +1,431 @@
+/*
+Minetest
+Copyright (C) 2022 x2048, Dmitry Kostenko <codeforsmile@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 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, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+
+#include <vector>
+#include <memory>
+#include <string>
+
+class RenderSource;
+class RenderTarget;
+class RenderStep;
+class Client;
+class Hud;
+class ShadowRenderer;
+
+struct PipelineContext
+{
+       PipelineContext(IrrlichtDevice *_device, Client *_client, Hud *_hud, ShadowRenderer *_shadow_renderer, video::SColor _color, v2u32 _target_size)
+               : device(_device), client(_client), hud(_hud), shadow_renderer(_shadow_renderer), clear_color(_color), target_size(_target_size)
+       {
+       }
+
+       IrrlichtDevice *device;
+       Client *client;
+       Hud *hud;
+       ShadowRenderer *shadow_renderer;
+       video::SColor clear_color;
+       v2u32 target_size;
+
+       bool show_hud {true};
+       bool show_minimap {true};
+       bool draw_wield_tool {true};
+       bool draw_crosshair {true};
+};
+
+/**
+ * Base object that can be owned by RenderPipeline
+ * 
+ */
+class RenderPipelineObject
+{
+public:
+       virtual ~RenderPipelineObject() = default;
+       virtual void reset(PipelineContext &context) {}
+};
+
+/**
+ * Represents a source of rendering information such as textures
+ */
+class RenderSource : virtual public RenderPipelineObject
+{
+public:
+       /**
+        * Return the number of textures in the source.
+        */
+       virtual u8 getTextureCount() = 0;
+
+       /**
+        * Get a texture by index. 
+        * Returns nullptr is the texture does not exist.
+        */
+       virtual video::ITexture *getTexture(u8 index) = 0;
+};
+
+/**
+ *     Represents a render target (screen or framebuffer)
+ */
+class RenderTarget : virtual public RenderPipelineObject
+{
+public:
+       /**
+        * Activate the render target and configure OpenGL state for the output.
+        * This is usually done by @see RenderStep implementations.
+        */
+       virtual void activate(PipelineContext &context)
+       {
+               m_clear = false;
+       }
+
+       /**
+        * Resets the state of the object for the next pipeline iteration
+        */
+       virtual void reset(PipelineContext &context) override
+       {
+               m_clear = true;
+       }
+
+protected:
+       bool m_clear {true};
+};
+
+/**
+ * Texture buffer represents a framebuffer with a multiple attached textures.
+ *
+ * @note Use of TextureBuffer requires use of gl_FragData[] in the shader
+ */
+class TextureBuffer : public RenderSource, public RenderTarget
+{
+public:
+       virtual ~TextureBuffer() override;
+
+       /**
+        * Configure fixed-size texture for the specific index
+        * 
+        * @param index index of the texture
+        * @param size width and height of the texture in pixels
+        * @param height height of the texture in pixels
+        * @param name unique name of the texture
+        * @param format color format
+        */
+       void setTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format);
+
+       /**
+        * Configure relative-size texture for the specific index
+        * 
+        * @param index index of the texture
+        * @param scale_factor relation of the texture dimensions to the screen dimensions
+        * @param name unique name of the texture
+        * @param format color format
+        */
+       void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format);
+
+       /**
+        * @Configure depth texture and assign index
+        * 
+        * @param index index to use for the depth texture
+        * @param size width and height of the texture in pixels
+        * @param name unique name for the texture
+        * @param format color format
+        */
+       void setDepthTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format);
+
+       /**
+        * @Configure depth texture and assign index
+        * 
+        * @param index index to use for the depth texture
+        * @param scale_factor relation of the texture dimensions to the screen dimensions
+        * @param name unique name for the texture
+        * @param format color format
+        */
+       void setDepthTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format);
+
+       virtual u8 getTextureCount() override { return m_textures.size(); }
+       virtual video::ITexture *getTexture(u8 index) override;
+       virtual void activate(PipelineContext &context) override;
+       virtual void reset(PipelineContext &context) override;
+private:
+       static const u8 NO_DEPTH_TEXTURE = 255;
+
+       struct TextureDefinition
+       {
+               bool valid { false };
+               bool fixed_size { false };
+               bool dirty { false };
+               v2f scale_factor;
+               core::dimension2du size;
+               std::string name;
+               video::ECOLOR_FORMAT format;
+       };
+
+       /**
+        * Make sure the texture in the given slot matches the texture definition given the current context.
+        * @param textureSlot address of the texture pointer to verify and populate.
+        * @param definition logical definition of the texture
+        * @param context current context of the rendering pipeline
+        * @return true if a new texture was created and put into the slot
+        * @return false if the slot was not modified
+        */
+       bool ensureTexture(video::ITexture **textureSlot, const TextureDefinition& definition, PipelineContext &context);
+
+       video::IVideoDriver *m_driver { nullptr };
+       std::vector<TextureDefinition> m_definitions;
+       core::array<video::ITexture *> m_textures;
+       video::ITexture *m_depth_texture { nullptr };
+       u8 m_depth_texture_index { NO_DEPTH_TEXTURE };
+       video::IRenderTarget *m_render_target { nullptr };
+};
+
+/**
+ * Targets output to designated texture in texture buffer
+ */
+class TextureBufferOutput : public RenderTarget
+{
+public:
+       TextureBufferOutput(TextureBuffer *buffer, u8 texture_index);
+       void activate(PipelineContext &context) override;
+private:
+       TextureBuffer *buffer;
+       u8 texture_index;
+};
+
+/**
+ * Allows remapping texture indicies in another RenderSource.
+ * 
+ * @note all unmapped indexes are passed through to the underlying render source.
+ */
+class RemappingSource : RenderSource
+{
+public:
+       RemappingSource(RenderSource *source)
+                       : m_source(source)
+       {}
+
+       /**
+        * Maps texture index to a different index in the dependent source.
+        * 
+        * @param index texture index as requested by the @see RenderStep.
+        * @param target_index matching texture index in the underlying @see RenderSource.
+        */
+       void setMapping(u8 index, u8 target_index)
+       {
+               if (index >= m_mappings.size()) {
+                       u8 start = m_mappings.size();
+                       m_mappings.resize(index);
+                       for (u8 i = start; i < m_mappings.size(); ++i)
+                               m_mappings[i] = i;
+               }
+
+               m_mappings[index] = target_index;
+       }
+
+       virtual u8 getTextureCount() override
+       {
+               return m_mappings.size();
+       }
+
+       virtual video::ITexture *getTexture(u8 index) override
+       {
+               if (index < m_mappings.size())
+                       index = m_mappings[index];
+
+               return m_source->getTexture(index);
+       }
+public:
+       RenderSource *m_source;
+       std::vector<u8> m_mappings;
+};
+
+class DynamicSource : public RenderSource
+{
+public:
+       bool isConfigured() { return upstream != nullptr; }
+       void setRenderSource(RenderSource *value) { upstream = value; }
+
+       /**
+        * Return the number of textures in the source.
+        */
+       virtual u8 getTextureCount() override;
+
+       /**
+        * Get a texture by index. 
+        * Returns nullptr is the texture does not exist.
+        */
+       virtual video::ITexture *getTexture(u8 index) override;
+private:
+       RenderSource *upstream { nullptr };
+};
+
+/**
+ * Implements direct output to screen framebuffer.
+ */
+class ScreenTarget : public RenderTarget
+{
+public:
+       virtual void activate(PipelineContext &context) override;
+       virtual void reset(PipelineContext &context) override;
+private:
+       core::dimension2du size;
+};
+
+class DynamicTarget : public RenderTarget
+{
+public:
+       bool isConfigured() { return upstream != nullptr; }
+       void setRenderTarget(RenderTarget *value) { upstream = value; }
+       virtual void activate(PipelineContext &context) override;
+private:
+       RenderTarget *upstream { nullptr };
+};
+
+/**
+ * Base class for rendering steps in the pipeline
+ */
+class RenderStep : virtual public RenderPipelineObject
+{
+public:
+       /**
+        * Assigns render source to this step.
+        * 
+        * @param source source of rendering information
+        */
+       virtual void setRenderSource(RenderSource *source) = 0;
+
+       /**
+        * Assigned render target to this step.
+        * 
+        * @param target render target to send output to.
+        */
+       virtual void setRenderTarget(RenderTarget *target) = 0;
+
+       /**
+        * Runs the step. This method is invoked by the pipeline.
+        */
+       virtual void run(PipelineContext &context) = 0;
+};
+
+/**
+ * Provides default empty implementation of supporting methods in a rendering step.
+ */
+class TrivialRenderStep : public RenderStep
+{
+public:
+       virtual void setRenderSource(RenderSource *source) override {}
+       virtual void setRenderTarget(RenderTarget *target) override {}
+       virtual void reset(PipelineContext &) override {}
+};
+
+/**
+ * Dynamically changes render target of another step.
+ * 
+ * This allows re-running parts of the pipeline with different outputs
+ */
+class SetRenderTargetStep : public TrivialRenderStep
+{
+public:
+       SetRenderTargetStep(RenderStep *step, RenderTarget *target);
+       virtual void run(PipelineContext &context) override;
+private:
+       RenderStep *step;
+       RenderTarget *target;
+};
+
+/**
+ * Render Pipeline provides a flexible way to execute rendering steps in the engine.
+ * 
+ * RenderPipeline also implements @see RenderStep, allowing for nesting of the pipelines.
+ */
+class RenderPipeline : public RenderStep
+{
+public:
+       /**
+        * Add a step to the end of the pipeline
+        * 
+        * @param step reference to a @see RenderStep implementation.
+        */
+       RenderStep *addStep(RenderStep *step)
+       {
+               m_pipeline.push_back(step);
+               return step;
+       }
+
+       /**
+        * Capture ownership of a dynamically created @see RenderStep instance.
+        * 
+        * RenderPipeline will delete the instance when the pipeline is destroyed.
+        * 
+        * @param step reference to the instance.
+        * @return RenderStep* value of the 'step' parameter.
+        */
+       template<typename T>
+       T *own(std::unique_ptr<T> &&object)
+       {
+               T* result = object.release();
+               m_objects.push_back(std::unique_ptr<RenderPipelineObject>(result));
+               return result;
+       }
+
+       /**
+        * Create a new object that will be managed by the pipeline
+        *
+        * @tparam T type of the object to be created
+        * @tparam Args types of constructor arguments
+        * @param args constructor arguments
+        * @return T* pointer to the newly created object
+        */
+       template<typename T, typename... Args>
+       T *createOwned(Args&&... args) {
+               return own(std::make_unique<T>(std::forward<Args>(args)...));
+       }
+
+       /**
+        * Create and add a step managed by the pipeline and return a pointer
+        * to the step for further configuration.
+        *
+        * @tparam T Type of the step to be added.
+        * @tparam Args Types of the constructor parameters
+        * @param args Constructor parameters
+        * @return RenderStep* Pointer to the created step for further configuration.
+        */
+       template<typename T, typename... Args>
+       RenderStep *addStep(Args&&... args) {
+               T* result = own(std::make_unique<T>(std::forward<Args>(args)...));
+               return addStep(result);
+       }
+
+       RenderSource *getInput();
+       RenderTarget *getOutput();
+
+       v2f getScale() { return scale; }
+       void setScale(v2f value) { scale = value; }
+
+       virtual void reset(PipelineContext &context) override {}
+       virtual void run(PipelineContext &context) override;
+
+       virtual void setRenderSource(RenderSource *source) override;
+       virtual void setRenderTarget(RenderTarget *target) override;
+private:
+       std::vector<RenderStep *> m_pipeline;
+       std::vector< std::unique_ptr<RenderPipelineObject> > m_objects;
+       DynamicSource m_input;
+       DynamicTarget m_output;
+       v2f scale { 1.0f, 1.0f };
+};
index a130a14eb0256a6d926e1587873aa2471bb16119..88d2f9b190b3d07292a98d90dcc0bf67ca46de70 100644 (file)
@@ -19,58 +19,134 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "plain.h"
-#include "settings.h"
+#include "secondstage.h"
+#include "client/camera.h"
+#include "client/client.h"
+#include "client/clientmap.h"
+#include "client/hud.h"
+#include "client/minimap.h"
+#include "client/shadows/dynamicshadowsrender.h"
 
-inline u32 scaledown(u32 coef, u32 size)
+/// Draw3D pipeline step
+void Draw3D::run(PipelineContext &context)
 {
-       return (size + coef - 1) / coef;
+       if (m_target)
+               m_target->activate(context);
+
+       context.device->getSceneManager()->drawAll();
+       context.device->getVideoDriver()->setTransform(video::ETS_WORLD, core::IdentityMatrix);
+       if (!context.show_hud)
+               return;
+       context.hud->drawBlockBounds();
+       context.hud->drawSelectionMesh();
+       if (context.draw_wield_tool)
+               context.client->getCamera()->drawWieldedTool();
 }
 
-RenderingCorePlain::RenderingCorePlain(
-       IrrlichtDevice *_device, Client *_client, Hud *_hud)
-       : RenderingCore(_device, _client, _hud)
+void DrawHUD::run(PipelineContext &context)
 {
-       scale = g_settings->getU16("undersampling");
+       if (context.show_hud) {
+               if (context.shadow_renderer)
+                       context.shadow_renderer->drawDebug();
+
+               if (context.draw_crosshair)
+                       context.hud->drawCrosshair();
+
+               context.hud->drawHotbar(context.client->getEnv().getLocalPlayer()->getWieldIndex());
+               context.hud->drawLuaElements(context.client->getCamera()->getOffset());
+               context.client->getCamera()->drawNametags();
+               auto mapper = context.client->getMinimap();
+               if (mapper && context.show_minimap)
+                       mapper->drawMinimap();
+       }
+       context.device->getGUIEnvironment()->drawAll();
 }
 
-void RenderingCorePlain::initTextures()
+
+void MapPostFxStep::setRenderTarget(RenderTarget * _target)
 {
-       if (scale <= 1)
-               return;
-       v2u32 size{scaledown(scale, screensize.X), scaledown(scale, screensize.Y)};
-       lowres = driver->addRenderTargetTexture(
-                       size, "render_lowres", video::ECF_A8R8G8B8);
+       target = _target;
 }
 
-void RenderingCorePlain::clearTextures()
+void MapPostFxStep::run(PipelineContext &context)
 {
-       if (scale <= 1)
-               return;
-       driver->removeTexture(lowres);
+       if (target)
+               target->activate(context);
+
+       context.client->getEnv().getClientMap().renderPostFx(context.client->getCamera()->getCameraMode());
 }
 
-void RenderingCorePlain::beforeDraw()
+void RenderShadowMapStep::run(PipelineContext &context)
 {
-       if (scale <= 1)
-               return;
-       driver->setRenderTarget(lowres, true, true, skycolor);
+       // This is necessary to render shadows for animations correctly
+       context.device->getSceneManager()->getRootSceneNode()->OnAnimate(context.device->getTimer()->getTime());
+       context.shadow_renderer->update();
 }
 
-void RenderingCorePlain::upscale()
+// class UpscaleStep
+
+void UpscaleStep::run(PipelineContext &context)
 {
-       if (scale <= 1)
-               return;
-       driver->setRenderTarget(0, true, true);
-       v2u32 size{scaledown(scale, screensize.X), scaledown(scale, screensize.Y)};
-       v2u32 dest_size{scale * size.X, scale * size.Y};
-       driver->draw2DImage(lowres, core::rect<s32>(0, 0, dest_size.X, dest_size.Y),
-                       core::rect<s32>(0, 0, size.X, size.Y));
+       video::ITexture *lowres = m_source->getTexture(0);
+       m_target->activate(context);
+       context.device->getVideoDriver()->draw2DImage(lowres,
+                       core::rect<s32>(0, 0, context.target_size.X, context.target_size.Y),
+                       core::rect<s32>(0, 0, lowres->getSize().Width, lowres->getSize().Height));
+}
+
+std::unique_ptr<RenderStep> create3DStage(Client *client, v2f scale)
+{
+       RenderStep *step = new Draw3D();
+       if (g_settings->getBool("enable_shaders")) {
+               RenderPipeline *pipeline = new RenderPipeline();
+               pipeline->addStep(pipeline->own(std::unique_ptr<RenderStep>(step)));
+
+               auto effect = addPostProcessing(pipeline, step, scale, client);
+               effect->setRenderTarget(pipeline->getOutput());
+               step = pipeline;
+       }
+       return std::unique_ptr<RenderStep>(step);
 }
 
-void RenderingCorePlain::drawAll()
+static v2f getDownscaleFactor()
 {
-       draw3D();
-       drawPostFx();
-       upscale();
-       drawHUD();
+       u16 undersampling = MYMAX(g_settings->getU16("undersampling"), 1);
+       return v2f(1.0f / undersampling);
+}
+
+RenderStep* addUpscaling(RenderPipeline *pipeline, RenderStep *previousStep, v2f downscale_factor)
+{
+       const int TEXTURE_UPSCALE = 0;
+
+       if (downscale_factor.X == 1.0f && downscale_factor.Y == 1.0f)
+               return previousStep;
+
+       // Initialize buffer
+       TextureBuffer *buffer = pipeline->createOwned<TextureBuffer>();
+       buffer->setTexture(TEXTURE_UPSCALE, downscale_factor, "upscale", video::ECF_A8R8G8B8);
+
+       // Attach previous step to the buffer
+       TextureBufferOutput *buffer_output = pipeline->createOwned<TextureBufferOutput>(buffer, TEXTURE_UPSCALE);
+       previousStep->setRenderTarget(buffer_output);
+
+       // Add upscaling step
+       RenderStep *upscale = pipeline->createOwned<UpscaleStep>();
+       upscale->setRenderSource(buffer);
+       pipeline->addStep(upscale);
+
+       return upscale;
+}
+
+void populatePlainPipeline(RenderPipeline *pipeline, Client *client)
+{
+       auto downscale_factor = getDownscaleFactor();
+       auto step3D = pipeline->own(create3DStage(client, downscale_factor));
+       pipeline->addStep(step3D);
+       pipeline->addStep<MapPostFxStep>();
+
+       step3D = addUpscaling(pipeline, step3D, downscale_factor);
+
+       step3D->setRenderTarget(pipeline->createOwned<ScreenTarget>());
+
+       pipeline->addStep<DrawHUD>();
 }
index 80c17ed9f96daf511ebc3374a1214f8f4edf3347..5180304a44322377e75eec1896b5bdde5b266b8e 100644 (file)
@@ -20,19 +20,70 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 #include "core.h"
+#include "pipeline.h"
 
-class RenderingCorePlain : public RenderingCore
+/**
+ * Implements a pipeline step that renders the 3D scene
+ */
+class Draw3D : public RenderStep
 {
-protected:
-       int scale = 0;
-       video::ITexture *lowres = nullptr;
+public:
+       virtual void setRenderSource(RenderSource *) override {}
+       virtual void setRenderTarget(RenderTarget *target) override { m_target = target; }
+
+       virtual void reset(PipelineContext &context) override {}
+       virtual void run(PipelineContext &context) override;
+
+private:
+       RenderTarget *m_target {nullptr};
+};
+
+/**
+ * Implements a pipeline step that renders the game HUD
+ */
+class DrawHUD : public RenderStep
+{
+public:
+       virtual void setRenderSource(RenderSource *) override {}
+       virtual void setRenderTarget(RenderTarget *) override {}
+
+       virtual void reset(PipelineContext &context) override {}
+       virtual void run(PipelineContext &context) override;
+};
+
+class MapPostFxStep : public TrivialRenderStep
+{
+public:
+       virtual void setRenderTarget(RenderTarget *) override;
+       virtual void run(PipelineContext &context) override;
+private:
+       RenderTarget *target;
+};
 
-       void initTextures() override;
-       void clearTextures() override;
-       void beforeDraw() override;
-       void upscale();
+class RenderShadowMapStep : public TrivialRenderStep
+{
+public:
+       virtual void run(PipelineContext &context) override;
+};
 
+/**
+ * UpscaleStep step performs rescaling of the image 
+ * in the source texture 0 to the size of the target.
+ */
+class UpscaleStep : public RenderStep
+{
 public:
-       RenderingCorePlain(IrrlichtDevice *_device, Client *_client, Hud *_hud);
-       void drawAll() override;
+
+    virtual void setRenderSource(RenderSource *source) override { m_source = source; }
+    virtual void setRenderTarget(RenderTarget *target) override { m_target = target; }
+    virtual void reset(PipelineContext &context) override {};
+    virtual void run(PipelineContext &context) override;
+private:
+       RenderSource *m_source;
+       RenderTarget *m_target;
 };
+
+std::unique_ptr<RenderStep> create3DStage(Client *client, v2f scale);
+RenderStep* addUpscaling(RenderPipeline *pipeline, RenderStep *previousStep, v2f downscale_factor);
+
+void populatePlainPipeline(RenderPipeline *pipeline, Client *client);
diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp
new file mode 100644 (file)
index 0000000..8f6f595
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>
+Copyright (C) 2020 appgurueu, Lars Mueller <appgurulars@gmx.de>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 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, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "secondstage.h"
+#include "client/client.h"
+#include "client/shader.h"
+#include "client/tile.h"
+
+PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector<u8> &_texture_map) :
+       shader_id(_shader_id), texture_map(_texture_map)
+{
+       assert(texture_map.size() <= video::MATERIAL_MAX_TEXTURES);
+       configureMaterial();
+}
+
+void PostProcessingStep::configureMaterial()
+{
+       material.UseMipMaps = false;
+       material.ZBuffer = true;
+       material.ZWriteEnable = video::EZW_ON;
+       for (u32 k = 0; k < texture_map.size(); ++k) {
+               material.TextureLayer[k].AnisotropicFilter = false;
+               material.TextureLayer[k].BilinearFilter = false;
+               material.TextureLayer[k].TrilinearFilter = false;
+               material.TextureLayer[k].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
+               material.TextureLayer[k].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
+       }
+}
+
+void PostProcessingStep::setRenderSource(RenderSource *_source)
+{
+       source = _source;
+}
+
+void PostProcessingStep::setRenderTarget(RenderTarget *_target)
+{
+       target = _target;
+}
+
+void PostProcessingStep::reset(PipelineContext &context)
+{
+}
+
+void PostProcessingStep::run(PipelineContext &context)
+{
+       if (target)
+               target->activate(context);
+
+       // attach the shader
+       material.MaterialType = context.client->getShaderSource()->getShaderInfo(shader_id).material;
+
+       auto driver = context.device->getVideoDriver();
+
+       for (u32 i = 0; i < texture_map.size(); i++)
+               material.TextureLayer[i].Texture = source->getTexture(texture_map[i]);
+
+       static const video::SColor color = video::SColor(0, 0, 0, 255);
+       static const video::S3DVertex vertices[4] = {
+                       video::S3DVertex(1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
+                                       color, 1.0, 0.0),
+                       video::S3DVertex(-1.0, -1.0, 0.0, 0.0, 0.0, -1.0,
+                                       color, 0.0, 0.0),
+                       video::S3DVertex(-1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
+                                       color, 0.0, 1.0),
+                       video::S3DVertex(1.0, 1.0, 0.0, 0.0, 0.0, -1.0,
+                                       color, 1.0, 1.0),
+       };
+       static const u16 indices[6] = {0, 1, 2, 2, 3, 0};
+       driver->setMaterial(material);
+       driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2);
+}
+
+RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client)
+{
+       auto buffer = pipeline->createOwned<TextureBuffer>();
+       static const u8 TEXTURE_COLOR = 0;
+       static const u8 TEXTURE_DEPTH = 3;
+
+       // init post-processing buffer
+       buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", video::ECF_A8R8G8B8);
+
+       video::ECOLOR_FORMAT depth_format = video::ECF_D16; // fallback depth format
+       auto driver = client->getSceneManager()->getVideoDriver();
+       if (driver->queryTextureFormat(video::ECF_D32))
+               depth_format = video::ECF_D32;
+       else if (driver->queryTextureFormat(video::ECF_D24S8))
+               depth_format = video::ECF_D24S8;
+       buffer->setDepthTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format);
+
+       // attach buffer to the previous step
+       previousStep->setRenderTarget(buffer);
+
+       // post-processing stage
+       // set up shader
+       u32 shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH);
+
+       RenderStep *effect = pipeline->addStep<PostProcessingStep>(shader_id, std::vector<u8> { TEXTURE_COLOR });
+       effect->setRenderSource(buffer);
+       return effect;
+}
diff --git a/src/client/render/secondstage.h b/src/client/render/secondstage.h
new file mode 100644 (file)
index 0000000..0c3c0a4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017 numzero, Lobachevskiy Vitaliy <numzer0@yandex.ru>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 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, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#pragma once
+#include "stereo.h"
+#include "pipeline.h"
+
+class PostProcessingStep : public RenderStep
+{
+public:
+       PostProcessingStep(u32 shader_id, const std::vector<u8> &texture_map);
+
+       
+       void setRenderSource(RenderSource *source) override;
+       void setRenderTarget(RenderTarget *target) override;
+       void reset(PipelineContext &context) override;
+       void run(PipelineContext &context) override;
+
+private:
+       u32 shader_id;
+       std::vector<u8> texture_map;
+       RenderSource *source { nullptr };
+       RenderTarget *target { nullptr };
+       video::SMaterial material;
+
+       void configureMaterial();
+};
+
+RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client);
index ed08810db2627f968b9e9be6aa8051472597d296..7f199e17b81ed6486ae5c96ea44f6cbec6d9bd54 100644 (file)
@@ -19,56 +19,73 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "sidebyside.h"
-#include <ICameraSceneNode.h>
 #include "client/hud.h"
+#include "client/camera.h"
 
-RenderingCoreSideBySide::RenderingCoreSideBySide(
-       IrrlichtDevice *_device, Client *_client, Hud *_hud, bool _horizontal, bool _flipped)
-       : RenderingCoreStereo(_device, _client, _hud), horizontal(_horizontal), flipped(_flipped)
-{
-}
+DrawImageStep::DrawImageStep(u8 texture_index, v2f _offset) :
+       texture_index(texture_index), offset(_offset)
+{}
 
-void RenderingCoreSideBySide::initTextures()
+void DrawImageStep::setRenderSource(RenderSource *_source)
 {
-       if (horizontal) {
-               image_size = {screensize.X, screensize.Y / 2};
-               rpos = v2s32(0, screensize.Y / 2);
-       } else {
-               image_size = {screensize.X / 2, screensize.Y};
-               rpos = v2s32(screensize.X / 2, 0);
-       }
-       virtual_size = image_size;
-       left = driver->addRenderTargetTexture(
-                       image_size, "3d_render_left", video::ECF_A8R8G8B8);
-       right = driver->addRenderTargetTexture(
-                       image_size, "3d_render_right", video::ECF_A8R8G8B8);
+       source = _source;
 }
-
-void RenderingCoreSideBySide::clearTextures()
+void DrawImageStep::setRenderTarget(RenderTarget *_target)
 {
-       driver->removeTexture(left);
-       driver->removeTexture(right);
+       target = _target;
 }
 
-void RenderingCoreSideBySide::drawAll()
+void DrawImageStep::run(PipelineContext &context)
 {
-       driver->OnResize(image_size); // HACK to make GUI smaller
-       renderBothImages();
-       driver->OnResize(screensize);
-       driver->draw2DImage(left, {});
-       driver->draw2DImage(right, rpos);
+       if (target)
+               target->activate(context);
+       
+       auto texture = source->getTexture(texture_index);
+       core::dimension2du output_size = context.device->getVideoDriver()->getScreenSize();
+       v2s32 pos(offset.X * output_size.Width, offset.Y * output_size.Height);
+       context.device->getVideoDriver()->draw2DImage(texture, pos);
 }
 
-void RenderingCoreSideBySide::useEye(bool _right)
+void populateSideBySidePipeline(RenderPipeline *pipeline, Client *client, bool horizontal, bool flipped, v2f &virtual_size_scale)
 {
-       driver->setRenderTarget(_right ? right : left, true, true, skycolor);
-       RenderingCoreStereo::useEye(_right ^ flipped);
-}
+       static const u8 TEXTURE_LEFT = 0;
+       static const u8 TEXTURE_RIGHT = 1;
 
-void RenderingCoreSideBySide::resetEye()
-{
-       hud->resizeHotbar();
-       drawHUD();
-       driver->setRenderTarget(nullptr, false, false, skycolor);
-       RenderingCoreStereo::resetEye();
-}
+       v2f offset;
+       if (horizontal) {
+               virtual_size_scale = v2f(1.0f, 0.5f);
+               offset = v2f(0.0f, 0.5f);
+       }
+       else {
+               virtual_size_scale = v2f(0.5f, 1.0f);
+               offset = v2f(0.5f, 0.0f);
+       }
+
+       TextureBuffer *buffer = pipeline->createOwned<TextureBuffer>();
+       buffer->setTexture(TEXTURE_LEFT, virtual_size_scale, "3d_render_left", video::ECF_A8R8G8B8);
+       buffer->setTexture(TEXTURE_RIGHT, virtual_size_scale, "3d_render_right", video::ECF_A8R8G8B8);
+
+       auto step3D = pipeline->own(create3DStage(client, virtual_size_scale));
+
+       // eyes
+       for (bool right : { false, true }) {
+               pipeline->addStep<OffsetCameraStep>(flipped ? !right : right);
+               auto output = pipeline->createOwned<TextureBufferOutput>(buffer, right ? TEXTURE_RIGHT : TEXTURE_LEFT);
+               pipeline->addStep<SetRenderTargetStep>(step3D, output);
+               pipeline->addStep(step3D);
+               pipeline->addStep<MapPostFxStep>();
+               pipeline->addStep<DrawHUD>();
+       }
+
+       pipeline->addStep<OffsetCameraStep>(0.0f);
+
+       auto screen = pipeline->createOwned<ScreenTarget>();
+
+       for (bool right : { false, true }) {
+               auto step = pipeline->addStep<DrawImageStep>(
+                               right ? TEXTURE_RIGHT : TEXTURE_LEFT,
+                               right ? offset : v2f());
+               step->setRenderSource(buffer);
+               step->setRenderTarget(screen);
+       }
+}
\ No newline at end of file
index f8ed256b3aefd1a86e9c0187dad433e5285b7285..edf76ea8f5cd6b9993b4171eda48691cd4297a32 100644 (file)
@@ -21,23 +21,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #pragma once
 #include "stereo.h"
 
-class RenderingCoreSideBySide : public RenderingCoreStereo
+class DrawImageStep : public RenderStep
 {
-protected:
-       video::ITexture *left = nullptr;
-       video::ITexture *right = nullptr;
-       bool horizontal = false;
-       bool flipped = false;
-       core::dimension2du image_size;
-       v2s32 rpos;
-
-       void initTextures() override;
-       void clearTextures() override;
-       void useEye(bool right) override;
-       void resetEye() override;
-
 public:
-       RenderingCoreSideBySide(IrrlichtDevice *_device, Client *_client, Hud *_hud,
-                       bool _horizontal = false, bool _flipped = false);
-       void drawAll() override;
+       DrawImageStep(u8 texture_index, v2f offset);
+
+       void setRenderSource(RenderSource *_source) override;
+       void setRenderTarget(RenderTarget *_target) override;
+
+       void reset(PipelineContext &context) override {}
+       void run(PipelineContext &context) override;
+private:
+       u8 texture_index;
+       v2f offset;
+       RenderSource *source;
+       RenderTarget *target;
 };
+
+void populateSideBySidePipeline(RenderPipeline *pipeline, Client *client, bool horizontal, bool flipped, v2f &virtual_size_scale);
\ No newline at end of file
index 0f54e166e941879a7b8b973110cd0a587f4d9a8b..8d8d62db1e3678d6d819134e31f0255053726a41 100644 (file)
@@ -19,42 +19,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "stereo.h"
+#include "client/client.h"
 #include "client/camera.h"
 #include "constants.h"
 #include "settings.h"
 
-RenderingCoreStereo::RenderingCoreStereo(
-       IrrlichtDevice *_device, Client *_client, Hud *_hud)
-       : RenderingCore(_device, _client, _hud)
+OffsetCameraStep::OffsetCameraStep(float eye_offset)
 {
-       eye_offset = BS * g_settings->getFloat("3d_paralax_strength", -0.087f, 0.087f);
+       move.setTranslation(core::vector3df(eye_offset, 0.0f, 0.0f));
 }
 
-void RenderingCoreStereo::beforeDraw()
-{
-       cam = camera->getCameraNode();
-       base_transform = cam->getRelativeTransformation();
-}
 
-void RenderingCoreStereo::useEye(bool right)
+OffsetCameraStep::OffsetCameraStep(bool right_eye)
 {
-       core::matrix4 move;
-       move.setTranslation(
-                       core::vector3df(right ? eye_offset : -eye_offset, 0.0f, 0.0f));
-       cam->setPosition((base_transform * move).getTranslation());
+       float eye_offset = BS * g_settings->getFloat("3d_paralax_strength", -0.087f, 0.087f) * (right_eye ? 1 : -1);
+       move.setTranslation(core::vector3df(eye_offset, 0.0f, 0.0f));
 }
 
-void RenderingCoreStereo::resetEye()
+void OffsetCameraStep::reset(PipelineContext &context)
 {
-       cam->setPosition(base_transform.getTranslation());
+       base_transform = context.client->getCamera()->getCameraNode()->getRelativeTransformation();
 }
 
-void RenderingCoreStereo::renderBothImages()
+void OffsetCameraStep::run(PipelineContext &context)
 {
-       useEye(false);
-       draw3D();
-       resetEye();
-       useEye(true);
-       draw3D();
-       resetEye();
+       context.client->getCamera()->getCameraNode()->setPosition((base_transform * move).getTranslation());
 }
index c8b07e146b4ceeddff6460fed4b692a29f799f1b..f0fa6accc15210904dc04799e151f035350a2a47 100644 (file)
@@ -20,19 +20,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 #include "core.h"
+#include "plain.h"
+#include "pipeline.h"
 
-class RenderingCoreStereo : public RenderingCore
-{
-protected:
-       scene::ICameraSceneNode *cam;
-       core::matrix4 base_transform;
-       float eye_offset;
-
-       void beforeDraw() override;
-       virtual void useEye(bool right);
-       virtual void resetEye();
-       void renderBothImages();
 
+/**
+ * Offset camera for a specific eye in stereo rendering mode
+ */
+class OffsetCameraStep : public TrivialRenderStep
+{
 public:
-       RenderingCoreStereo(IrrlichtDevice *_device, Client *_client, Hud *_hud);
+       OffsetCameraStep(float eye_offset);
+       OffsetCameraStep(bool right_eye);
+
+       void run(PipelineContext &context) override;
+       void reset(PipelineContext &context) override;
+private:
+       core::matrix4 base_transform;
+       core::matrix4 move;
 };
index 009a4b3d706c48d27d72119e36dc9dcd59e3aab1..e836d013115b9d935bbc8f8f2fa3f4a7bebf81a3 100644 (file)
@@ -214,13 +214,13 @@ class MainShaderConstantSetter : public IShaderConstantSetter
        CachedVertexShaderSetting<f32, 16> m_world;
 
        // Shadow-related
-       CachedPixelShaderSetting<f32, 16> m_shadow_view_proj;
+       CachedPixelShaderSetting<f32, 16, false> m_shadow_view_proj;
        CachedPixelShaderSetting<f32, 3> m_light_direction;
        CachedPixelShaderSetting<f32> m_texture_res;
        CachedPixelShaderSetting<f32> m_shadow_strength;
        CachedPixelShaderSetting<f32> m_time_of_day;
        CachedPixelShaderSetting<f32> m_shadowfar;
-       CachedPixelShaderSetting<f32, 4> m_camera_pos;
+       CachedPixelShaderSetting<f32, 4, false> m_camera_pos;
        CachedPixelShaderSetting<s32> m_shadow_texture;
        CachedVertexShaderSetting<f32> m_perspective_bias0_vertex;
        CachedPixelShaderSetting<f32> m_perspective_bias0_pixel;
index 49a5631154a9b59a1632d534423a6e74b9b73a85..8f1ba1e4153cc87e21062eab6637ab34616d54d9 100644 (file)
@@ -80,7 +80,7 @@ class IShaderConstantSetterFactory {
 };
 
 
-template <typename T, std::size_t count=1>
+template <typename T, std::size_t count, bool cache>
 class CachedShaderSetting {
        const char *m_name;
        T m_sent[count];
@@ -93,30 +93,32 @@ class CachedShaderSetting {
 public:
        void set(const T value[count], video::IMaterialRendererServices *services)
        {
-               if (has_been_set && std::equal(m_sent, m_sent + count, value))
+               if (cache && has_been_set && std::equal(m_sent, m_sent + count, value))
                        return;
                if (is_pixel)
                        services->setPixelShaderConstant(services->getPixelShaderConstantID(m_name), value, count);
                else
                        services->setVertexShaderConstant(services->getVertexShaderConstantID(m_name), value, count);
 
-               std::copy(value, value + count, m_sent);
-               has_been_set = true;
+               if (cache) {
+                       std::copy(value, value + count, m_sent);
+                       has_been_set = true;
+               }
        }
 };
 
-template <typename T, std::size_t count = 1>
-class CachedPixelShaderSetting : public CachedShaderSetting<T, count> {
+template <typename T, std::size_t count = 1, bool cache=true>
+class CachedPixelShaderSetting : public CachedShaderSetting<T, count, cache> {
 public:
        CachedPixelShaderSetting(const char *name) :
-               CachedShaderSetting<T, count>(name, true){}
+               CachedShaderSetting<T, count, cache>(name, true){}
 };
 
-template <typename T, std::size_t count = 1>
-class CachedVertexShaderSetting : public CachedShaderSetting<T, count> {
+template <typename T, std::size_t count = 1, bool cache=true>
+class CachedVertexShaderSetting : public CachedShaderSetting<T, count, cache> {
 public:
        CachedVertexShaderSetting(const char *name) :
-               CachedShaderSetting<T, count>(name, false){}
+               CachedShaderSetting<T, count, cache>(name, false){}
 };
 
 
index 944deb801ff8e82737b9c22e6be4f8c75cb44bd9..fd3841d2d87719ca2bea048c6e60aaa6226cf688 100644 (file)
@@ -707,3 +707,22 @@ std::string ShadowRenderer::readShaderFile(const std::string &path)
 
        return prefix + content;
 }
+
+ShadowRenderer *createShadowRenderer(IrrlichtDevice *device, Client *client)
+{
+       // disable if unsupported
+       if (g_settings->getBool("enable_dynamic_shadows") && (
+               g_settings->get("video_driver") != "opengl" ||
+               !g_settings->getBool("enable_shaders"))) {
+               g_settings->setBool("enable_dynamic_shadows", false);
+       }
+
+       if (g_settings->getBool("enable_shaders") &&
+                       g_settings->getBool("enable_dynamic_shadows")) {
+               ShadowRenderer *shadow_renderer = new ShadowRenderer(device, client);
+               shadow_renderer->initialize();
+               return shadow_renderer;
+       }
+
+       return nullptr;
+}
index bd27f6f20046da5e6f1b6dd76481aecb7c871be8..79dd25c447aba52fa8b9126f13b2fd2763c77591 100644 (file)
@@ -160,3 +160,12 @@ class ShadowRenderer
        shadowScreenQuad *m_screen_quad{nullptr};
        shadowScreenQuadCB *m_shadow_mix_cb{nullptr};
 };
+
+/**
+ * @brief Create a shadow renderer if settings allow this.
+ * 
+ * @param device Device to be used to render shadows.
+ * @param client Reference to the client context.
+ * @return A new ShadowRenderer instance or nullptr if shadows are disabled or not supported.
+ */
+ShadowRenderer *createShadowRenderer(IrrlichtDevice *device, Client *client);