From 3ac96d2670b3e689d406ff5b5c683d6a4c67c8bd Mon Sep 17 00:00:00 2001 From: number Zero Date: Wed, 16 Aug 2017 16:51:18 +0300 Subject: [PATCH] Interlaced 3D --- .../3d_interlaced_merge/opengl_fragment.glsl | 21 ++ .../3d_interlaced_merge/opengl_vertex.glsl | 6 + src/client/renderingcore.cpp | 266 ++++++++++++++++-- src/client/renderingcore.h | 86 ++++-- src/client/renderingengine.cpp | 6 +- 5 files changed, 335 insertions(+), 50 deletions(-) create mode 100644 client/shaders/3d_interlaced_merge/opengl_fragment.glsl create mode 100644 client/shaders/3d_interlaced_merge/opengl_vertex.glsl diff --git a/client/shaders/3d_interlaced_merge/opengl_fragment.glsl b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl new file mode 100644 index 00000000..25945ad7 --- /dev/null +++ b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl @@ -0,0 +1,21 @@ +uniform sampler2D baseTexture; +uniform sampler2D normalTexture; +uniform sampler2D textureFlags; + +#define leftImage baseTexture +#define rightImage normalTexture +#define maskImage textureFlags + +void main(void) +{ + vec2 uv = gl_TexCoord[0].st; + vec4 left = texture2D(leftImage, uv).rgba; + vec4 right = texture2D(rightImage, uv).rgba; + vec4 mask = texture2D(maskImage, uv).rgba; + vec4 color; + if (mask.r > 0.5) + color = right; + else + color = left; + gl_FragColor = color; +} diff --git a/client/shaders/3d_interlaced_merge/opengl_vertex.glsl b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl new file mode 100644 index 00000000..4e0b2b12 --- /dev/null +++ b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl @@ -0,0 +1,6 @@ +void main(void) +{ + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = gl_Vertex; + gl_FrontColor = gl_BackColor = gl_Color; +} diff --git a/src/client/renderingcore.cpp b/src/client/renderingcore.cpp index 9e7af791..14b88684 100644 --- a/src/client/renderingcore.cpp +++ b/src/client/renderingcore.cpp @@ -11,6 +11,11 @@ RenderingCore::RenderingCore(irr::IrrlichtDevice *_device) : device(_device), driver(device->getVideoDriver()), smgr(device->getSceneManager()) +{ + screensize = driver->getScreenSize(); +} + +void RenderingCore::update_screen_size() { } @@ -26,7 +31,10 @@ void RenderingCore::setup(Camera *_camera, Client *_client, LocalPlayer *_player mapper = _mapper; guienv = _guienv; - screensize = _screensize; + if (screensize != _screensize) { + screensize = _screensize; + update_screen_size(); + } skycolor = _skycolor; show_hud = _show_hud; show_minimap = _show_minimap; @@ -94,17 +102,32 @@ RenderingCoreStereo::RenderingCoreStereo(irr::IrrlichtDevice *_device) : parallax_strength = g_settings->getFloat("3d_paralax_strength"); } -void RenderingCoreStereo::use_eye(Eye eye) +void RenderingCoreStereo::pre_draw() { - irr::scene::ICameraSceneNode *cam = camera->getCameraNode(); - irr::core::matrix4 transform = cam->getRelativeTransformation(); - irr::core::matrix4 move; - move.setTranslation(irr::core::vector3df(static_cast(eye) * parallax_strength, 0.0f, 0.0f)); - cam->setPosition((transform * move).getTranslation()); + cam = camera->getCameraNode(); + base_transform = cam->getRelativeTransformation(); } -void RenderingCoreStereo::use_default() +void RenderingCoreStereo::use_eye(bool right) { + irr::core::matrix4 move; + move.setTranslation(irr::core::vector3df(right ? parallax_strength : -parallax_strength, 0.0f, 0.0f)); + cam->setPosition((base_transform * move).getTranslation()); +} + +void RenderingCoreStereo::reset_eye() +{ + cam->setPosition(base_transform.getTranslation()); +} + +void RenderingCoreStereo::draw_two() +{ + use_eye(false); + draw_3d(); + reset_eye(); + use_eye(true); + draw_3d(); + reset_eye(); } RenderingCoreAnaglyph::RenderingCoreAnaglyph(irr::IrrlichtDevice *_device) : @@ -114,28 +137,18 @@ RenderingCoreAnaglyph::RenderingCoreAnaglyph(irr::IrrlichtDevice *_device) : void RenderingCoreAnaglyph::draw() { - use_eye(Eye::Left); - draw_3d(); - use_eye(Eye::Right); - draw_3d(); - use_default(); + pre_draw(); + draw_two(); draw_last_fx(); draw_hud(); } -void RenderingCoreAnaglyph::use_eye(Eye eye) +void RenderingCoreAnaglyph::use_eye(bool right) { - RenderingCoreStereo::use_eye(eye); + RenderingCoreStereo::use_eye(right); driver->clearZBuffer(); irr::video::SOverrideMaterial &mat = driver->getOverrideMaterial(); - switch (eye) { - case Eye::Left: - mat.Material.ColorMask = irr::video::ECP_RED; - break; - case Eye::Right: - mat.Material.ColorMask = irr::video::ECP_GREEN | irr::video::ECP_BLUE; - break; - } + mat.Material.ColorMask = right ? irr::video::ECP_GREEN | irr::video::ECP_BLUE : irr::video::ECP_RED; mat.EnableFlags = irr::video::EMF_COLOR_MASK; mat.EnablePasses = irr::scene::ESNRP_SKY_BOX | irr::scene::ESNRP_SOLID | @@ -143,9 +156,8 @@ void RenderingCoreAnaglyph::use_eye(Eye eye) irr::scene::ESNRP_TRANSPARENT_EFFECT | irr::scene::ESNRP_SHADOW; } -void RenderingCoreAnaglyph::use_default() +void RenderingCoreAnaglyph::reset_eye() { - RenderingCoreStereo::use_default(); irr::video::SOverrideMaterial &mat = driver->getOverrideMaterial(); mat.Material.ColorMask = irr::video::ECP_ALL; mat.EnableFlags = irr::video::EMF_COLOR_MASK; @@ -155,20 +167,220 @@ void RenderingCoreAnaglyph::use_default() irr::scene::ESNRP_TRANSPARENT_EFFECT + irr::scene::ESNRP_SHADOW; } -RenderingCoreDouble::RenderingCoreDouble(irr::IrrlichtDevice *_device, RenderingCoreDouble::Mode _mode) : +RenderingCoreSideBySide::RenderingCoreSideBySide(irr::IrrlichtDevice *_device) : RenderingCoreStereo(_device) { + init_textures(); } -void RenderingCoreDouble::draw() +RenderingCoreSideBySide::~RenderingCoreSideBySide() { + clear_textures(); +} + +void RenderingCoreSideBySide::init_textures() +{ + image_size = v2u32(screensize.X / 2, screensize.Y); + left = driver->addRenderTargetTexture(image_size, "3d_render_left", irr::video::ECF_A8R8G8B8); + right = driver->addRenderTargetTexture(image_size, "3d_render_right", irr::video::ECF_A8R8G8B8); + hud = driver->addRenderTargetTexture(screensize, "3d_render_hud", irr::video::ECF_A8R8G8B8); +} + +void RenderingCoreSideBySide::clear_textures() +{ + driver->removeTexture(left); + driver->removeTexture(right); + driver->removeTexture(hud); +} + +void RenderingCoreSideBySide::update_screen_size() +{ + clear_textures(); + init_textures(); +} + +void RenderingCoreSideBySide::draw() +{ + pre_draw(); + draw_two(); + driver->setRenderTarget(hud, true, true, video::SColor(0, 0, 0, 0)); + draw_hud(); + driver->setRenderTarget(nullptr, false, false, skycolor); + + driver->draw2DImage(left, v2s32(0, 0)); + driver->draw2DImage(right, v2s32(screensize.X / 2, 0)); + + driver->draw2DImage(hud, + irr::core::rect(0, 0, screensize.X / 2, screensize.Y), + irr::core::rect(0, 0, screensize.X, screensize.Y), 0, 0, + true); + + driver->draw2DImage(hud, + irr::core::rect(screensize.X / 2, 0, screensize.X, screensize.Y), + irr::core::rect(0, 0, screensize.X, screensize.Y), 0, 0, + true); +} + +void RenderingCoreSideBySide::use_eye(bool _right) +{ + driver->setRenderTarget(_right ? right : left, true, true, skycolor); + RenderingCoreStereo::use_eye(_right); +} + +void RenderingCoreSideBySide::reset_eye() +{ + driver->setRenderTarget(nullptr, false, false, skycolor); + RenderingCoreStereo::reset_eye(); +} + +RenderingCorePageflip::RenderingCorePageflip(irr::IrrlichtDevice *_device) : + RenderingCoreStereo(_device) +{ + init_textures(); +} + +RenderingCorePageflip::~RenderingCorePageflip() +{ + clear_textures(); +} + +void RenderingCorePageflip::init_textures() +{ + hud = driver->addRenderTargetTexture(screensize, "3d_render_hud", irr::video::ECF_A8R8G8B8); +} + +void RenderingCorePageflip::clear_textures() +{ + driver->removeTexture(hud); +} + +void RenderingCorePageflip::update_screen_size() +{ + clear_textures(); + init_textures(); +} + +void RenderingCorePageflip::draw() +{ + pre_draw(); + driver->setRenderTarget(hud, true, true, video::SColor(0, 0, 0, 0)); + draw_hud(); + driver->setRenderTarget(nullptr, false, false, skycolor); + draw_two(); +} + +void RenderingCorePageflip::use_eye(bool _right) +{ + driver->setRenderTarget( + _right ? irr::video::ERT_STEREO_RIGHT_BUFFER : irr::video::ERT_STEREO_LEFT_BUFFER, + true, true, skycolor); + RenderingCoreStereo::use_eye(_right); +} + +void RenderingCorePageflip::reset_eye() +{ + driver->draw2DImage(hud, v2s32(0, 0)); + driver->setRenderTarget(irr::video::ERT_FRAME_BUFFER, false, false, skycolor); + RenderingCoreStereo::reset_eye(); } RenderingCoreInterlaced::RenderingCoreInterlaced(irr::IrrlichtDevice *_device) : RenderingCoreStereo(_device) { + mat.MaterialType = (video::E_MATERIAL_TYPE)0; + init_textures(); +} + +RenderingCoreInterlaced::~RenderingCoreInterlaced() +{ + clear_textures(); +} + +void RenderingCoreInterlaced::init_material() +{ + if (mat.MaterialType) + return; + IShaderSource *s = client->getShaderSource(); + mat.UseMipMaps = false; + mat.ZBuffer = false; + mat.ZWriteEnable = false; + u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC, 0); + 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 = irr::video::ETC_CLAMP_TO_EDGE; + mat.TextureLayer[k].TextureWrapV = irr::video::ETC_CLAMP_TO_EDGE; + } +} + +void RenderingCoreInterlaced::init_textures() +{ + image_size = v2u32(screensize.X, screensize.Y / 2); + left = driver->addRenderTargetTexture(image_size, "3d_render_left", irr::video::ECF_A8R8G8B8); + right = driver->addRenderTargetTexture(image_size, "3d_render_right", irr::video::ECF_A8R8G8B8); + mask = driver->addTexture(screensize, "3d_render_mask", irr::video::ECF_A8R8G8B8); + init_mask(); + mat.TextureLayer[0].Texture = left; + mat.TextureLayer[1].Texture = right; + mat.TextureLayer[2].Texture = mask; +} + +void RenderingCoreInterlaced::clear_textures() +{ + driver->removeTexture(left); + driver->removeTexture(right); + driver->removeTexture(mask); +} + +void RenderingCoreInterlaced::init_mask() +{ + u8 *data = reinterpret_cast(mask->lock()); + for (unsigned j = 0; j < screensize.Y; j++) { + u8 val = j % 2 ? 0xff : 0x00; + memset(data, val, 4 * screensize.X); + data += 4 * screensize.X; + } + mask->unlock(); +} + +void RenderingCoreInterlaced::update_screen_size() +{ + clear_textures(); + init_textures(); } void RenderingCoreInterlaced::draw() { + pre_draw(); + draw_two(); + merge(); + draw_hud(); +} + +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 }; + init_material(); + driver->setMaterial(mat); + driver->drawVertexPrimitiveList(&vertices, 4, &indices, 2); +} + +void RenderingCoreInterlaced::use_eye(bool _right) +{ + driver->setRenderTarget(_right ? right : left, true, true, skycolor); + RenderingCoreStereo::use_eye(_right); +} + +void RenderingCoreInterlaced::reset_eye() +{ + driver->setRenderTarget(nullptr, false, false, skycolor); + RenderingCoreStereo::reset_eye(); } diff --git a/src/client/renderingcore.h b/src/client/renderingcore.h index 80147100..958ce837 100644 --- a/src/client/renderingcore.h +++ b/src/client/renderingcore.h @@ -48,11 +48,13 @@ protected: irr::scene::ISceneManager *smgr; irr::gui::IGUIEnvironment *guienv; + virtual void update_screen_size(); + public: RenderingCore(irr::IrrlichtDevice *_device); RenderingCore(const RenderingCore &) = delete; RenderingCore(RenderingCore &&) = delete; - ~RenderingCore() = default; + virtual ~RenderingCore() = default; RenderingCore &operator= (const RenderingCore &) = delete; RenderingCore &operator= (RenderingCore &&) = delete; @@ -78,45 +80,91 @@ public: class RenderingCoreStereo: public RenderingCore { protected: + irr::scene::ICameraSceneNode *cam; + irr::core::matrix4 base_transform; float parallax_strength; + void pre_draw(); + virtual void use_eye(bool right); + virtual void reset_eye(); + void draw_two(); + public: - enum class Eye - { - Left = -1, - Right = 1, - }; RenderingCoreStereo(irr::IrrlichtDevice *_device); - virtual void use_eye(Eye eye); - virtual void use_default(); }; class RenderingCoreAnaglyph: public RenderingCoreStereo { +protected: + void use_eye(bool right) override; + void reset_eye() override; + public: RenderingCoreAnaglyph(irr::IrrlichtDevice *_device); void draw() override; - void use_eye(Eye eye) override; - void use_default() override; }; -class RenderingCoreDouble: public RenderingCoreStereo +class RenderingCoreSideBySide: public RenderingCoreStereo { -public: - enum class Mode - { - SideBySide, - TopBottom, - Pageflip - }; +protected: + v2u32 image_size; + video::ITexture *left = nullptr; + video::ITexture *right = nullptr; + video::ITexture *hud = nullptr; - RenderingCoreDouble(irr::IrrlichtDevice *_device, Mode _mode); + void init_textures(); + void clear_textures(); + + void update_screen_size() override; + void use_eye(bool right) override; + void reset_eye() override; + +public: + RenderingCoreSideBySide(irr::IrrlichtDevice *_device); + ~RenderingCoreSideBySide() override; + void draw() override; +}; + +class RenderingCorePageflip: public RenderingCoreStereo +{ +protected: + video::ITexture *hud = nullptr; + + void init_textures(); + void clear_textures(); + + void update_screen_size() override; + void use_eye(bool right) override; + void reset_eye() override; + +public: + RenderingCorePageflip(irr::IrrlichtDevice *_device); + ~RenderingCorePageflip() override; void draw() override; }; class RenderingCoreInterlaced: public RenderingCoreStereo { +protected: + v2u32 image_size; + video::ITexture *left = nullptr; + video::ITexture *right = nullptr; + video::ITexture *mask = nullptr; + video::SMaterial mat; + + void init_material(); + void init_textures(); + void clear_textures(); + void init_mask(); + + void update_screen_size() override; + void use_eye(bool right) override; + void reset_eye() override; + + void merge(); + public: RenderingCoreInterlaced(irr::IrrlichtDevice *_device); + ~RenderingCoreInterlaced() override; void draw() override; }; diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 8dd30274..77ae9925 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -107,11 +107,9 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) else if (draw_mode == "interlaced") core.reset(new RenderingCoreInterlaced(m_device)); else if (draw_mode == "sidebyside") - core.reset(new RenderingCoreDouble(m_device, RenderingCoreDouble::Mode::SideBySide)); - else if (draw_mode == "topbottom") - core.reset(new RenderingCoreDouble(m_device, RenderingCoreDouble::Mode::TopBottom)); + core.reset(new RenderingCoreSideBySide(m_device)); else if (draw_mode == "pageflip") - core.reset(new RenderingCoreDouble(m_device, RenderingCoreDouble::Mode::Pageflip)); + core.reset(new RenderingCorePageflip(m_device)); else core.reset(new RenderingCorePlain(m_device));