From 5876849eb137fe772ea376af366dae3fd2a9e427 Mon Sep 17 00:00:00 2001 From: Renato Machado Date: Tue, 21 Nov 2023 20:17:54 -0300 Subject: [PATCH] feat: Shader with framebuffer --- README.md | 3 ++ modules/game_shaders/shaders.lua | 19 ++++++----- .../shaders/fragment/outline.frag | 23 +++++++++++++ src/client/shadermanager.cpp | 15 +++++---- src/client/shadermanager.h | 6 ++-- src/client/thingtype.cpp | 25 ++++++++++++-- src/client/thingtype.h | 1 + src/framework/graphics/drawpool.cpp | 33 +++++++++++++++++++ src/framework/graphics/drawpool.h | 4 +++ src/framework/graphics/drawpoolmanager.cpp | 28 ---------------- src/framework/graphics/drawpoolmanager.h | 7 ++-- src/framework/graphics/framebuffermanager.cpp | 14 ++++++-- src/framework/graphics/framebuffermanager.h | 6 ++-- src/framework/graphics/paintershaderprogram.h | 10 ++++++ 14 files changed, 139 insertions(+), 55 deletions(-) create mode 100644 modules/game_shaders/shaders/fragment/outline.frag diff --git a/README.md b/README.md index 0dff7cde12..28a09dadfa 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,9 @@ - QR Code support, with auto generate it from string [@conde2] - qr-code-border: 2 - qr-code: Hail OTClient Redemption - Conde2 Dev + +##### Sponsored (Features) +- Shader with Framebuffer | ([@SkullzOTS](https://github.com/SkullzOTS), [@Mryukiimaru](https://github.com/Mryukiimaru), [@JeanTheOne](https://github.com/JeanTheOne), [@KizaruHere](https://github.com/KizaruHere)) ##### [OTClient V8](https://github.com/OTCv8) (Features) - Lighting System diff --git a/modules/game_shaders/shaders.lua b/modules/game_shaders/shaders.lua index c1a9d908d7..694ff71866 100644 --- a/modules/game_shaders/shaders.lua +++ b/modules/game_shaders/shaders.lua @@ -1,5 +1,5 @@ HOTKEY = 'Ctrl+Y' -MAP_SHADERS = {{ +MAP_SHADERS = { { name = 'Map - Default', frag = nil }, { @@ -47,9 +47,9 @@ MAP_SHADERS = {{ }, { name = 'Map - Noise', frag = 'shaders/fragment/noise.frag' -}} +} } -OUTFIT_SHADERS = {{ +OUTFIT_SHADERS = { { name = 'Outfit - Default', frag = nil }, { @@ -65,15 +65,19 @@ OUTFIT_SHADERS = {{ }, { name = 'Outfit - Fragmented', frag = 'shaders/fragment/noise.frag' -}} +}, { + name = 'Outfit - Outline', + useFramebuffer = true, + frag = 'shaders/fragment/outline.frag' +} } -MOUNT_SHADERS = {{ +MOUNT_SHADERS = { { name = 'Mount - Default', frag = nil }, { name = 'Mount - Rainbow', frag = 'shaders/fragment/party.frag' -}} +} } -- Fix for texture offset drawing, adding walking offsets. local dirs = { @@ -172,11 +176,10 @@ function init() local registerShader = function(opts, method) local fragmentShaderPath = resolvepath(opts.frag) - local vertexShaderPath = resolvepath(opts.frag ~= nil and opts.vert or 'shaders/core/vertex/default.vert') if fragmentShaderPath ~= nil then -- local shader = g_shaders.createShader() - g_shaders.createFragmentShader(opts.name, opts.frag) + g_shaders.createFragmentShader(opts.name, opts.frag, opts.useFramebuffer or false) if opts.tex1 then g_shaders.addMultiTexture(opts.name, opts.tex1) diff --git a/modules/game_shaders/shaders/fragment/outline.frag b/modules/game_shaders/shaders/fragment/outline.frag new file mode 100644 index 0000000000..bc60a4dc42 --- /dev/null +++ b/modules/game_shaders/shaders/fragment/outline.frag @@ -0,0 +1,23 @@ +const float offset = 1.0 / 64.0; +uniform float u_Time; +uniform sampler2D u_Tex0; +varying vec2 v_TexCoord; + +void main() +{ + vec4 col = texture2D(u_Tex0, v_TexCoord); + if (col.a > 0.5) + gl_FragColor = col; + else { + float a = texture2D(u_Tex0, vec2(v_TexCoord.x + offset, v_TexCoord.y)).a + + texture2D(u_Tex0, vec2(v_TexCoord.x, v_TexCoord.y - offset)).a + + texture2D(u_Tex0, vec2(v_TexCoord.x - offset, v_TexCoord.y)).a + + texture2D(u_Tex0, vec2(v_TexCoord.x, v_TexCoord.y + offset)).a; + if (col.a < 1.0 && a > 0.0) { + float x = (cos(u_Time * 9.57) + 1.0)/2.0 * 0.2 + 0.8; + gl_FragColor = vec4(x, x, x, x); + } else { + gl_FragColor = col; + } + } +} \ No newline at end of file diff --git a/src/client/shadermanager.cpp b/src/client/shadermanager.cpp index c5939ff823..b61cb2693b 100644 --- a/src/client/shadermanager.cpp +++ b/src/client/shadermanager.cpp @@ -32,20 +32,22 @@ ShaderManager g_shaders; void ShaderManager::init() { PainterShaderProgram::release(); } void ShaderManager::terminate() { m_shaders.clear(); } -void ShaderManager::createShader(const std::string_view name) +void ShaderManager::createShader(const std::string_view name, bool useFramebuffer) { - g_mainDispatcher.addEvent([&, name = name.data()] { + g_mainDispatcher.addEvent([this, name = name.data(), useFramebuffer] { const auto& shader = std::make_shared(); + shader->setUseFramebuffer(useFramebuffer); m_shaders[name] = shader; return shader; }); } -void ShaderManager::createFragmentShader(const std::string_view name, const std::string_view file) +void ShaderManager::createFragmentShader(const std::string_view name, const std::string_view file, bool useFramebuffer) { const auto& filePath = g_resources.resolvePath(file.data()); - g_mainDispatcher.addEvent([&, name = name.data(), filePath] { + g_mainDispatcher.addEvent([this, name = name.data(), filePath, useFramebuffer] { const auto& shader = std::make_shared(); + shader->setUseFramebuffer(useFramebuffer); if (!shader) return; @@ -66,10 +68,11 @@ void ShaderManager::createFragmentShader(const std::string_view name, const std: }); } -void ShaderManager::createFragmentShaderFromCode(const std::string_view name, const std::string_view code) +void ShaderManager::createFragmentShaderFromCode(const std::string_view name, const std::string_view code, bool useFramebuffer) { - g_mainDispatcher.addEvent([&, name = name.data(), code = code.data()] { + g_mainDispatcher.addEvent([this, name = name.data(), code = code.data(), useFramebuffer] { const auto& shader = std::make_shared(); + shader->setUseFramebuffer(useFramebuffer); if (!shader) return; diff --git a/src/client/shadermanager.h b/src/client/shadermanager.h index 24927dec05..969b425452 100644 --- a/src/client/shadermanager.h +++ b/src/client/shadermanager.h @@ -49,9 +49,9 @@ class ShaderManager void setupOutfitShader(const std::string_view name); void setupMountShader(const std::string_view name); - void createShader(const std::string_view name); - void createFragmentShader(const std::string_view name, const std::string_view file); - void createFragmentShaderFromCode(const std::string_view name, const std::string_view code); + void createShader(const std::string_view name, bool useFramebuffer = false); + void createFragmentShader(const std::string_view name, const std::string_view file, bool useFramebuffer = false); + void createFragmentShaderFromCode(const std::string_view name, const std::string_view code, bool useFramebuffer = false); void addMultiTexture(const std::string_view name, const std::string_view file); diff --git a/src/client/thingtype.cpp b/src/client/thingtype.cpp index 55d04cc921..520eb1d1ab 100644 --- a/src/client/thingtype.cpp +++ b/src/client/thingtype.cpp @@ -603,6 +603,23 @@ void ThingType::unserializeOtml(const OTMLNodePtr& node) } } +void ThingType::drawWithFrameBuffer(const Point& dest, const TexturePtr& texture, Rect screenRect, const Rect& textureRect, const Color& color, const DrawConductor& conductor) { + const int size = static_cast(g_gameConfig.getSpriteSize() * m_size.area() * g_drawPool.getScaleFactor()); + const auto& p = Point(size / 2); + const auto& destDiff = Rect(dest - p, Size{ size }); + + g_drawPool.bindFrameBuffer(destDiff.size()); { + g_drawPool.resetShaderProgram(); + + // Debug + // g_drawPool.addBoundingRect(Rect(Point(0), destDiff.size()), Color::red); + + screenRect = Rect(p - (dest - screenRect.topLeft()), screenRect.size()); + g_drawPool.addTexturedRect(screenRect, texture, textureRect, color, conductor); + } g_drawPool.releaseFrameBuffer(destDiff); + g_drawPool.resetShaderProgram(); +} + void ThingType::draw(const Point& dest, int layer, int xPattern, int yPattern, int zPattern, int animationPhase, const Color& color, bool drawThings, LightView* lightView, const DrawConductor& conductor) { if (m_null) @@ -628,7 +645,11 @@ void ThingType::draw(const Point& dest, int layer, int xPattern, int yPattern, i if (drawThings) { const auto& newColor = m_opacity < 1.0f ? Color(color, m_opacity) : color; - g_drawPool.addTexturedRect(screenRect, texture, textureRect, newColor, conductor); + + if (g_drawPool.shaderNeedFramebuffer()) + drawWithFrameBuffer(dest, texture, screenRect, textureRect, newColor, conductor); + else + g_drawPool.addTexturedRect(screenRect, texture, textureRect, newColor, conductor); } if (lightView && hasLight()) { @@ -1048,4 +1069,4 @@ void ThingType::exportImage(const std::string& fileName) image->savePNG(fileName); } -#endif +#endif \ No newline at end of file diff --git a/src/client/thingtype.h b/src/client/thingtype.h index 3ef27e5a6f..300dc5a08c 100644 --- a/src/client/thingtype.h +++ b/src/client/thingtype.h @@ -279,6 +279,7 @@ class ThingType : public LuaObject #endif void draw(const Point& dest, int layer, int xPattern, int yPattern, int zPattern, int animationPhase, const Color& color, bool drawThings = true, LightView* lightView = nullptr, const DrawConductor& conductor = DEFAULT_DRAW_CONDUCTOR); + void drawWithFrameBuffer(const Point& dest, const TexturePtr& texture, Rect screenRect, const Rect& textureRect, const Color& color, const DrawConductor& conductor); uint16_t getId() { return m_id; } ThingCategory getCategory() { return m_category; } diff --git a/src/framework/graphics/drawpool.cpp b/src/framework/graphics/drawpool.cpp index 62a731c974..f8a5c23816 100644 --- a/src/framework/graphics/drawpool.cpp +++ b/src/framework/graphics/drawpool.cpp @@ -353,4 +353,37 @@ void DrawPool::setFramebuffer(const Size& size) { void DrawPool::removeFramebuffer() { m_status.first = 0; m_framebuffer = nullptr; +} + +void DrawPool::addAction(const std::function& action) +{ + const uint8_t order = m_type == DrawPoolType::MAP ? DrawOrder::THIRD : DrawOrder::FIRST; + m_objects[m_depthLevel][order].emplace_back(action); +} + +void DrawPool::bindFrameBuffer(const Size& size) +{ + const uint8_t frameIndex = m_type == DrawPoolType::MAP ? 0 : 1; + + m_oldState = std::move(m_state); + m_state = {}; + addAction([size, frameIndex, drawState = m_state] { + drawState.execute(); + const auto& frame = g_framebuffers.getTemporaryFrameBuffer(frameIndex); + frame->resize(size); + frame->bind(); + }); +} +void DrawPool::releaseFrameBuffer(const Rect& dest) +{ + const uint8_t frameIndex = m_type == DrawPoolType::MAP ? 0 : 1; + + m_state = std::move(m_oldState); + addAction([dest, frameIndex, drawState = m_state] { + const auto& frame = g_framebuffers.getTemporaryFrameBuffer(frameIndex); + frame->release(); + drawState.execute(); + frame->draw(dest); + }); + if (hasFrameBuffer() && !dest.isNull()) stdext::hash_union(m_status.second, dest.hash()); } \ No newline at end of file diff --git a/src/framework/graphics/drawpool.h b/src/framework/graphics/drawpool.h index 7e85f64643..3de84471f2 100644 --- a/src/framework/graphics/drawpool.h +++ b/src/framework/graphics/drawpool.h @@ -183,6 +183,10 @@ class DrawPool DrawMode drawMode = DrawMode::TRIANGLES, const DrawConductor& conductor = DEFAULT_DRAW_CONDUCTOR, const CoordsBufferPtr& coordsBuffer = nullptr); + void addAction(const std::function& action); + void bindFrameBuffer(const Size& size); + void releaseFrameBuffer(const Rect& dest); + inline void setFPS(uint16_t fps) { m_refreshDelay = fps; } void updateHash(const DrawPool::DrawMethod& method, const TexturePtr& texture, const Color& color); diff --git a/src/framework/graphics/drawpoolmanager.cpp b/src/framework/graphics/drawpoolmanager.cpp index 754a26e856..623b54262f 100644 --- a/src/framework/graphics/drawpoolmanager.cpp +++ b/src/framework/graphics/drawpoolmanager.cpp @@ -207,34 +207,6 @@ void DrawPoolManager::addBoundingRect(const Rect& dest, const Color& color, uint getCurrentPool()->add(color, nullptr, method); } -void DrawPoolManager::addAction(const std::function& action) const -{ - getCurrentPool()->m_objects[0][DrawOrder::FIRST].emplace_back(action); -} - -void DrawPoolManager::bindFrameBuffer(const Size& size) const -{ - getCurrentPool()->m_oldState = std::move(getCurrentPool()->m_state); - getCurrentPool()->m_state = {}; - - g_drawPool.addAction([size, drawState = getCurrentPool()->m_state] { - drawState.execute(); - const auto& frame = g_framebuffers.getTemporaryFrameBuffer(); - frame->resize(size); - frame->bind(); - }); -} -void DrawPoolManager::releaseFrameBuffer(const Rect& dest) const -{ - getCurrentPool()->m_state = std::move(getCurrentPool()->m_oldState); - g_drawPool.addAction([dest, drawState = getCurrentPool()->m_state] { - const auto& frame = g_framebuffers.getTemporaryFrameBuffer(); - frame->release(); - drawState.execute(); - frame->draw(dest); - }); -} - void DrawPoolManager::use(const DrawPoolType type, const Rect& dest, const Rect& src, const Color& colorClear) { select(type); diff --git a/src/framework/graphics/drawpoolmanager.h b/src/framework/graphics/drawpoolmanager.h index 2077fa4437..d52a008cd7 100644 --- a/src/framework/graphics/drawpoolmanager.h +++ b/src/framework/graphics/drawpoolmanager.h @@ -52,16 +52,17 @@ class DrawPoolManager void addFilledRect(const Rect& dest, const Color& color = Color::white, const DrawConductor& condutor = DEFAULT_DRAW_CONDUCTOR) const; void addFilledTriangle(const Point& a, const Point& b, const Point& c, const Color& color = Color::white) const; void addBoundingRect(const Rect& dest, const Color& color = Color::white, uint16_t innerLineWidth = 1) const; - void addAction(const std::function& action) const; + void addAction(const std::function& action) const { getCurrentPool()->addAction(action); } - void bindFrameBuffer(const Size& size) const; - void releaseFrameBuffer(const Rect& dest) const; + void bindFrameBuffer(const Size& size) const { getCurrentPool()->bindFrameBuffer(size); } + void releaseFrameBuffer(const Rect& dest) const { getCurrentPool()->releaseFrameBuffer(dest); }; void setOpacity(const float opacity, bool onlyOnce = false) const { getCurrentPool()->setOpacity(opacity, onlyOnce); } void setClipRect(const Rect& clipRect, bool onlyOnce = false) const { getCurrentPool()->setClipRect(clipRect, onlyOnce); } void setBlendEquation(BlendEquation equation, bool onlyOnce = false) const { getCurrentPool()->setBlendEquation(equation, onlyOnce); } void setCompositionMode(const CompositionMode mode, bool onlyOnce = false) const { getCurrentPool()->setCompositionMode(mode, onlyOnce); } + bool shaderNeedFramebuffer() const { return getCurrentPool()->m_state.shaderProgram && getCurrentPool()->m_state.shaderProgram->useFramebuffer(); } void setShaderProgram(const PainterShaderProgramPtr& shaderProgram, const std::function& action) const { getCurrentPool()->setShaderProgram(shaderProgram, false, action); } void setShaderProgram(const PainterShaderProgramPtr& shaderProgram, bool onlyOnce = false, const std::function& action = nullptr) const { getCurrentPool()->setShaderProgram(shaderProgram, onlyOnce, action); } diff --git a/src/framework/graphics/framebuffermanager.cpp b/src/framework/graphics/framebuffermanager.cpp index d4127a93f8..f63f96bce6 100644 --- a/src/framework/graphics/framebuffermanager.cpp +++ b/src/framework/graphics/framebuffermanager.cpp @@ -21,13 +21,21 @@ */ #include "framebuffermanager.h" +#include "drawpoolmanager.h" FrameBufferManager g_framebuffers; void FrameBufferManager::init() { - m_temporaryFramebuffer = std::make_shared(); - m_temporaryFramebuffer->setSmooth(true); + m_temporaryFramebuffer.reserve(2); + for (uint_fast8_t i = 0; i < 2; ++i) { + const auto& frame = m_temporaryFramebuffer.emplace_back(std::make_shared()); + if (i == 0) { + frame->setSmooth(false); + } + }; } -void FrameBufferManager::terminate() { m_temporaryFramebuffer = nullptr; } \ No newline at end of file +void FrameBufferManager::terminate() { + m_temporaryFramebuffer.clear(); +} \ No newline at end of file diff --git a/src/framework/graphics/framebuffermanager.h b/src/framework/graphics/framebuffermanager.h index 6e56e5625b..0e710429b2 100644 --- a/src/framework/graphics/framebuffermanager.h +++ b/src/framework/graphics/framebuffermanager.h @@ -30,10 +30,12 @@ class FrameBufferManager void init(); void terminate(); - const FrameBufferPtr& getTemporaryFrameBuffer() { return m_temporaryFramebuffer; } + const FrameBufferPtr& getTemporaryFrameBuffer(const uint8_t index) const { + return m_temporaryFramebuffer[index]; + } protected: - FrameBufferPtr m_temporaryFramebuffer; + std::vector m_temporaryFramebuffer; }; extern FrameBufferManager g_framebuffers; diff --git a/src/framework/graphics/paintershaderprogram.h b/src/framework/graphics/paintershaderprogram.h index 89b67d7676..914ccdeffa 100644 --- a/src/framework/graphics/paintershaderprogram.h +++ b/src/framework/graphics/paintershaderprogram.h @@ -64,7 +64,17 @@ class PainterShaderProgram : public ShaderProgram void addMultiTexture(const std::string& file); void bindMultiTextures() const; + void setUseFramebuffer(bool v) { + m_useFramebuffer = v; + } + + bool useFramebuffer() const { + return m_useFramebuffer; + } + private: + bool m_useFramebuffer{ false }; + float m_startTime{ 0 }; float m_opacity{ 1.f }; float m_time{ 0 };