From 64039f0e431226117891754d830723ad0c31a5da Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 6 Apr 2025 12:47:25 +0300 Subject: [PATCH] add post-effect-slot resource & add gfx.posteffects library --- src/content/Content.hpp | 4 ++ src/content/content_fwd.hpp | 6 +- src/frontend/screens/LevelScreen.cpp | 8 ++- src/graphics/core/PostEffect.cpp | 61 ++++++++++++++++- src/graphics/core/PostEffect.hpp | 13 +++- src/graphics/core/PostProcessing.cpp | 11 ++- src/graphics/core/PostProcessing.hpp | 6 +- src/logic/scripting/lua/libs/api_lua.hpp | 3 +- .../scripting/lua/libs/libposteffects.cpp | 68 +++++++++++++++++++ src/logic/scripting/scripting_hud.cpp | 8 ++- src/logic/scripting/scripting_hud.hpp | 6 +- 11 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 src/logic/scripting/lua/libs/libposteffects.cpp diff --git a/src/content/Content.hpp b/src/content/Content.hpp index 94650b25..bbdf2013 100644 --- a/src/content/Content.hpp +++ b/src/content/Content.hpp @@ -189,6 +189,8 @@ constexpr const char* to_string(ResourceType type) { switch (type) { case ResourceType::CAMERA: return "camera"; + case ResourceType::POST_EFFECT_SLOT: + return "post-effect-slot"; default: return "unknown"; } @@ -197,6 +199,8 @@ constexpr const char* to_string(ResourceType type) { inline std::optional ResourceType_from(std::string_view str) { if (str == "camera") { return ResourceType::CAMERA; + } else if (str == "post-effect-slot") { + return ResourceType::POST_EFFECT_SLOT; } return std::nullopt; } diff --git a/src/content/content_fwd.hpp b/src/content/content_fwd.hpp index 076a433f..f6e2ffb7 100644 --- a/src/content/content_fwd.hpp +++ b/src/content/content_fwd.hpp @@ -7,7 +7,11 @@ class ContentPackRuntime; enum class ContentType { NONE, BLOCK, ITEM, ENTITY, GENERATOR }; -enum class ResourceType : size_t { CAMERA, LAST = CAMERA }; +enum class ResourceType : size_t { + CAMERA, + POST_EFFECT_SLOT, + LAST = POST_EFFECT_SLOT +}; inline constexpr auto RESOURCE_TYPES_COUNT = static_cast(ResourceType::LAST) + 1; diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index 44b81765..76f7e950 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -42,7 +42,9 @@ LevelScreen::LevelScreen( ) : Screen(engine), world(*levelPtr->getWorld()), - postProcessing(std::make_unique()), + postProcessing(std::make_unique( + levelPtr->content.getIndices(ResourceType::POST_EFFECT_SLOT).size() + )), gui(engine.getGUI()), input(engine.getInput()) { Level* level = levelPtr.get(); @@ -115,7 +117,9 @@ void LevelScreen::initializeContent() { for (auto& entry : content.getPacks()) { initializePack(entry.second.get()); } - scripting::on_frontend_init(hud.get(), renderer.get()); + scripting::on_frontend_init( + hud.get(), renderer.get(), postProcessing.get() + ); } void LevelScreen::initializePack(ContentPackRuntime* pack) { diff --git a/src/graphics/core/PostEffect.cpp b/src/graphics/core/PostEffect.cpp index 7fcc5162..d634c636 100644 --- a/src/graphics/core/PostEffect.cpp +++ b/src/graphics/core/PostEffect.cpp @@ -1,15 +1,16 @@ #include "PostEffect.hpp" #include "Shader.hpp" +#include "data/dv_util.hpp" PostEffect::Param::Param() : type(Type::FLOAT) {} PostEffect::Param::Param(Type type, Value defValue) - : type(type), defValue(std::move(defValue)) { + : type(type), defValue(defValue), value(defValue) { } PostEffect::PostEffect( - std::unique_ptr shader, + std::shared_ptr shader, std::unordered_map params ) : shader(std::move(shader)), params(std::move(params)) { @@ -18,9 +19,65 @@ PostEffect::PostEffect( Shader& PostEffect::use() { shader->use(); shader->uniform1f("u_intensity", intensity); + for (auto& [name, param] : params) { + if (!param.dirty) { + continue; + } + switch (param.type) { + case Param::Type::FLOAT: + shader->uniform1f(name, std::get(param.value)); + break; + case Param::Type::VEC2: + shader->uniform2f(name, std::get(param.value)); + break; + case Param::Type::VEC3: + shader->uniform3f(name, std::get(param.value)); + break; + case Param::Type::VEC4: + shader->uniform4f(name, std::get(param.value)); + break; + default: + assert(false); + } + param.dirty = false; + } return *shader; } +float PostEffect::getIntensity() const { + return intensity; +} + void PostEffect::setIntensity(float value) { intensity = value; } + +template +static void set_value(PostEffect::Param::Value& dst, const dv::value& value) { + glm::vec vec; + dv::get_vec(value, vec); + dst = vec; +} + +void PostEffect::setParam(const std::string& name, const dv::value& value) { + const auto& found = params.find(name); + if (found == params.end()) { + return; + } + auto& param = found->second; + switch (param.type) { + case Param::Type::FLOAT: + param.value = static_cast(value.asNumber()); + break; + case Param::Type::VEC2: + set_value<2>(param.value, value); + break; + case Param::Type::VEC3: + set_value<3>(param.value, value); + break; + case Param::Type::VEC4: + set_value<4>(param.value, value); + break; + } + param.dirty = true; +} diff --git a/src/graphics/core/PostEffect.hpp b/src/graphics/core/PostEffect.hpp index ed8f2753..4624ec40 100644 --- a/src/graphics/core/PostEffect.hpp +++ b/src/graphics/core/PostEffect.hpp @@ -6,6 +6,8 @@ #include #include +#include "data/dv_fwd.hpp" + class Shader; class PostEffect { @@ -16,25 +18,32 @@ public: Type type; Value defValue; + Value value; + bool dirty = true; Param(); Param(Type type, Value defValue); }; PostEffect( - std::unique_ptr shader, + std::shared_ptr shader, std::unordered_map params ); + explicit PostEffect(const PostEffect&) = default; + Shader& use(); + float getIntensity() const; void setIntensity(float value); + void setParam(const std::string& name, const dv::value& value); + bool isActive() { return intensity > 1e-4f; } private: - std::unique_ptr shader; + std::shared_ptr shader; std::unordered_map params; float intensity = 0.0f; }; diff --git a/src/graphics/core/PostProcessing.cpp b/src/graphics/core/PostProcessing.cpp index f41ad661..c15aa8fb 100644 --- a/src/graphics/core/PostProcessing.cpp +++ b/src/graphics/core/PostProcessing.cpp @@ -9,7 +9,8 @@ #include -PostProcessing::PostProcessing() { +PostProcessing::PostProcessing(size_t effectSlotsCount) + : effectSlots(effectSlotsCount) { // Fullscreen quad mesh bulding float vertices[] { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, @@ -77,6 +78,14 @@ void PostProcessing::render( } } +void PostProcessing::setEffect(size_t slot, std::shared_ptr effect) { + effectSlots.at(slot) = std::move(effect); +} + +PostEffect* PostProcessing::getEffect(size_t slot) { + return effectSlots.at(slot).get(); +} + std::unique_ptr PostProcessing::toImage() { return fbo->getTexture()->readData(); } diff --git a/src/graphics/core/PostProcessing.hpp b/src/graphics/core/PostProcessing.hpp index 888c6694..57a081c9 100644 --- a/src/graphics/core/PostProcessing.hpp +++ b/src/graphics/core/PostProcessing.hpp @@ -21,7 +21,7 @@ class PostProcessing { std::unique_ptr quadMesh; std::vector> effectSlots; public: - PostProcessing(); + PostProcessing(size_t effectSlotsCount); ~PostProcessing(); /// @brief Prepare and bind framebuffer @@ -34,6 +34,10 @@ public: /// @throws std::runtime_error if use(...) wasn't called before void render(const DrawContext& context, const Assets& assets, float timer); + void setEffect(size_t slot, std::shared_ptr effect); + + PostEffect* getEffect(size_t slot); + /// @brief Make an image from the last rendered frame std::unique_ptr toImage(); diff --git a/src/logic/scripting/lua/libs/api_lua.hpp b/src/logic/scripting/lua/libs/api_lua.hpp index 39d09241..7b4253c5 100644 --- a/src/logic/scripting/lua/libs/api_lua.hpp +++ b/src/logic/scripting/lua/libs/api_lua.hpp @@ -38,6 +38,7 @@ extern const luaL_Reg networklib[]; extern const luaL_Reg packlib[]; extern const luaL_Reg particleslib[]; // gfx.particles extern const luaL_Reg playerlib[]; +extern const luaL_Reg posteffectslib[]; // gfx.posteffects extern const luaL_Reg quatlib[]; extern const luaL_Reg text3dlib[]; // gfx.text3d extern const luaL_Reg timelib[]; @@ -46,7 +47,7 @@ extern const luaL_Reg utf8lib[]; extern const luaL_Reg vec2lib[]; // vecn.cpp extern const luaL_Reg vec3lib[]; // vecn.cpp extern const luaL_Reg vec4lib[]; // vecn.cpp -extern const luaL_Reg weatherlib[]; +extern const luaL_Reg weatherlib[]; // gfx.weather extern const luaL_Reg worldlib[]; extern const luaL_Reg yamllib[]; diff --git a/src/logic/scripting/lua/libs/libposteffects.cpp b/src/logic/scripting/lua/libs/libposteffects.cpp new file mode 100644 index 00000000..ccdb34aa --- /dev/null +++ b/src/logic/scripting/lua/libs/libposteffects.cpp @@ -0,0 +1,68 @@ +#include "libhud.hpp" + +#include "assets/Assets.hpp" +#include "content/Content.hpp" +#include "graphics/core/PostEffect.hpp" +#include "graphics/core/PostProcessing.hpp" + +using namespace scripting; + +static int l_index(lua::State* L) { + auto name = lua::require_string(L, 1); + auto& indices = content->getIndices(ResourceType::POST_EFFECT_SLOT); + return lua::pushinteger(L, indices.indexOf(name)); +} + +static int l_set_effect(lua::State* L) { + size_t index = static_cast(lua::tointeger(L, 1)); + auto name = lua::require_string(L, 2); + auto& assets = *engine->getAssets(); + auto effect = std::make_shared(assets.require(name)); + post_processing->setEffect(index, std::move(effect)); + return 0; +} + +static int l_get_intensity(lua::State* L) { + size_t index = static_cast(lua::tointeger(L, 1)); + auto effect = post_processing->getEffect(index); + return lua::pushnumber(L, effect ? effect->getIntensity() : 0.0f); +} + +static int l_set_intensity(lua::State* L) { + size_t index = static_cast(lua::tointeger(L, 1)); + float value = lua::tonumber(L, 2); + post_processing->getEffect(index)->setIntensity(value); + return 0; +} + +static int l_is_active(lua::State* L) { + size_t index = static_cast(lua::tointeger(L, 1)); + auto effect = post_processing->getEffect(index); + return lua::pushboolean(L, effect ? effect->isActive() : false); +} + +static int l_set_params(lua::State* L) { + size_t index = static_cast(lua::tointeger(L, 1)); + auto table = lua::tovalue(L, 2); + if (!table.isObject()) { + throw std::runtime_error("params table expected"); + } + auto effect = post_processing->getEffect(index); + if (effect == nullptr) { + return 0; + } + for (const auto& [key, value] : table.asObject()) { + effect->setParam(key, value); + } + return 0; +} + +const luaL_Reg posteffectslib[] = { + {"index", lua::wrap}, + {"set_effect", lua::wrap}, + {"get_intensity", lua::wrap}, + {"set_intensity", lua::wrap}, + {"is_active", lua::wrap}, + {"set_params", lua::wrap}, + {NULL, NULL} +}; diff --git a/src/logic/scripting/scripting_hud.cpp b/src/logic/scripting/scripting_hud.cpp index 305cb6f9..da72b031 100644 --- a/src/logic/scripting/scripting_hud.cpp +++ b/src/logic/scripting/scripting_hud.cpp @@ -19,6 +19,7 @@ static debug::Logger logger("scripting-hud"); Hud* scripting::hud = nullptr; WorldRenderer* scripting::renderer = nullptr; +PostProcessing* scripting::post_processing = nullptr; static void load_script(const std::string& name) { auto file = io::path("res:scripts") / name; @@ -28,9 +29,12 @@ static void load_script(const std::string& name) { lua::execute(lua::get_main_state(), 0, src, file.string()); } -void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) { +void scripting::on_frontend_init( + Hud* hud, WorldRenderer* renderer, PostProcessing* postProcessing +) { scripting::hud = hud; scripting::renderer = renderer; + scripting::post_processing = postProcessing; auto L = lua::get_main_state(); @@ -39,6 +43,7 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) { lua::openlib(L, "gfx", "particles", particleslib); lua::openlib(L, "gfx", "weather", weatherlib); lua::openlib(L, "gfx", "text3d", text3dlib); + lua::openlib(L, "gfx", "posteffects", posteffectslib); load_script("hud_classes.lua"); @@ -85,6 +90,7 @@ void scripting::on_frontend_close() { scripting::renderer = nullptr; scripting::hud = nullptr; + scripting::post_processing = nullptr; } void scripting::load_hud_script( diff --git a/src/logic/scripting/scripting_hud.hpp b/src/logic/scripting/scripting_hud.hpp index 1df47df2..ddfdc1e3 100644 --- a/src/logic/scripting/scripting_hud.hpp +++ b/src/logic/scripting/scripting_hud.hpp @@ -9,6 +9,7 @@ class Hud; class WorldRenderer; +class PostProcessing; namespace gui { class UINode; @@ -18,8 +19,11 @@ namespace gui { namespace scripting { extern Hud *hud; extern WorldRenderer* renderer; + extern PostProcessing* post_processing; - void on_frontend_init(Hud* hud, WorldRenderer* renderer); + void on_frontend_init( + Hud* hud, WorldRenderer* renderer, PostProcessing* postProcessing + ); void on_frontend_render(); void on_frontend_close();