Skip to content

Commit

Permalink
feat: Shader with framebuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
mehah authored Nov 21, 2023
1 parent 4190e86 commit 5876849
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 55 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 11 additions & 8 deletions modules/game_shaders/shaders.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
HOTKEY = 'Ctrl+Y'
MAP_SHADERS = {{
MAP_SHADERS = { {
name = 'Map - Default',
frag = nil
}, {
Expand Down Expand Up @@ -47,9 +47,9 @@ MAP_SHADERS = {{
}, {
name = 'Map - Noise',
frag = 'shaders/fragment/noise.frag'
}}
} }

OUTFIT_SHADERS = {{
OUTFIT_SHADERS = { {
name = 'Outfit - Default',
frag = nil
}, {
Expand All @@ -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 = {
Expand Down Expand Up @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions modules/game_shaders/shaders/fragment/outline.frag
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
15 changes: 9 additions & 6 deletions src/client/shadermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<PainterShaderProgram>();
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<PainterShaderProgram>();
shader->setUseFramebuffer(useFramebuffer);
if (!shader)
return;

Expand All @@ -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<PainterShaderProgram>();
shader->setUseFramebuffer(useFramebuffer);
if (!shader)
return;

Expand Down
6 changes: 3 additions & 3 deletions src/client/shadermanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
25 changes: 23 additions & 2 deletions src/client/thingtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(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)
Expand All @@ -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()) {
Expand Down Expand Up @@ -1048,4 +1069,4 @@ void ThingType::exportImage(const std::string& fileName)

image->savePNG(fileName);
}
#endif
#endif
1 change: 1 addition & 0 deletions src/client/thingtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
33 changes: 33 additions & 0 deletions src/framework/graphics/drawpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void()>& 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());
}
4 changes: 4 additions & 0 deletions src/framework/graphics/drawpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void()>& 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);
Expand Down
28 changes: 0 additions & 28 deletions src/framework/graphics/drawpoolmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<void()>& 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);
Expand Down
7 changes: 4 additions & 3 deletions src/framework/graphics/drawpoolmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<void()>& action) const;
void addAction(const std::function<void()>& 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<void()>& action) const { getCurrentPool()->setShaderProgram(shaderProgram, false, action); }
void setShaderProgram(const PainterShaderProgramPtr& shaderProgram, bool onlyOnce = false, const std::function<void()>& action = nullptr) const { getCurrentPool()->setShaderProgram(shaderProgram, onlyOnce, action); }

Expand Down
14 changes: 11 additions & 3 deletions src/framework/graphics/framebuffermanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,21 @@
*/

#include "framebuffermanager.h"
#include "drawpoolmanager.h"

FrameBufferManager g_framebuffers;

void FrameBufferManager::init()
{
m_temporaryFramebuffer = std::make_shared<FrameBuffer>();
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<FrameBuffer>());
if (i == 0) {
frame->setSmooth(false);
}
};
}

void FrameBufferManager::terminate() { m_temporaryFramebuffer = nullptr; }
void FrameBufferManager::terminate() {
m_temporaryFramebuffer.clear();
}
6 changes: 4 additions & 2 deletions src/framework/graphics/framebuffermanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<FrameBufferPtr> m_temporaryFramebuffer;
};

extern FrameBufferManager g_framebuffers;
10 changes: 10 additions & 0 deletions src/framework/graphics/paintershaderprogram.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down

0 comments on commit 5876849

Please sign in to comment.