diff --git a/CMakeLists.txt b/CMakeLists.txt index c6eb633..623b192 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.25) -set(BLT_GRAPHICS_VERSION 0.13.10) +set(BLT_GRAPHICS_VERSION 0.13.11) set(BLT_GRAPHICS_TEST_VERSION 0.0.1) project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION}) diff --git a/include/blt/gfx/renderer/batch_2d_renderer.h b/include/blt/gfx/renderer/batch_2d_renderer.h index 8ce7337..39d055e 100644 --- a/include/blt/gfx/renderer/batch_2d_renderer.h +++ b/include/blt/gfx/renderer/batch_2d_renderer.h @@ -216,7 +216,7 @@ namespace blt::gfx std::make_unique(state, frame_buffer_t::attachment_t::COLOR1), std::make_unique(frame_buffer_t::attachment_t::COLOR1, vec4{4, 4, 4, 1}), - std::make_unique(state, frame_buffer_t::attachment_t::COLOR1, 2, 2), + std::make_unique(frame_buffer_t::attachment_t::COLOR1, 2, 2), std::make_unique(state) ); } diff --git a/include/blt/gfx/renderer/postprocess.h b/include/blt/gfx/renderer/postprocess.h index 6e45a54..ffb3a12 100644 --- a/include/blt/gfx/renderer/postprocess.h +++ b/include/blt/gfx/renderer/postprocess.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include namespace blt::gfx { @@ -171,6 +173,27 @@ namespace blt::gfx i32 y_blur; }; + class pp_overlay_blur_step_t : public pp_in_place_t + { + public: + pp_overlay_blur_step_t(frame_buffer_t::attachment_t from, i32 x_blur = 2, i32 y_blur = 2): + pp_in_place_t(from), x_blur(x_blur), y_blur(y_blur) + {} + + pp_overlay_blur_step_t(frame_buffer_t::attachment_t from, frame_buffer_t::attachment_t to, i32 x_blur = 2, + i32 y_blur = 2): + pp_in_place_t(from, to), x_blur(x_blur), y_blur(y_blur) + {} + + void create() override; + + void draw(frame_buffer_t& previous); + + private: + i32 x_blur; + i32 y_blur; + }; + class pp_expansion_step_inplace_t : public pp_in_place_t { public: @@ -207,7 +230,7 @@ namespace blt::gfx steps.emplace_back(std::move(step)); } - static std::unique_ptr createShader(std::string_view fragment); + static std::unique_ptr createShader(std::string_view fragment, std::optional> engine = {}); static std::unique_ptr make_basic_pp() { diff --git a/include/blt/gfx/renderer/shaders/pp_overlay_blur.frag b/include/blt/gfx/renderer/shaders/pp_overlay_blur.frag new file mode 100644 index 0000000..e7d3dea --- /dev/null +++ b/include/blt/gfx/renderer/shaders/pp_overlay_blur.frag @@ -0,0 +1,32 @@ +#ifdef __cplusplus +#include +const std::string shader_overlay_blur_frag = R"(" +#version 300 es +precision mediump float; + +out vec4 FragColor; +in vec2 uv; +in vec2 pos; + +uniform sampler2D tex; +uniform ivec4 size; + +void main() { + vec2 texelSize = 1.0 / vec2(float(size.x), float(size.y)); + vec4 result = vec4(0.0, 0.0, 0.0, 0.0); + for (int x = -size.z; x <= size.z; ++x) { + for (int y = -size.w; y <= size.w; ++y) { + vec2 offset = (vec2(float(x), float(y)) * texelSize); + result += texture(tex, uv + offset); + } + } + vec4 blur_val = result / vec4((float(size.z) * 2.0) * (float(size.w) * 2.0)); + vec4 tex_val = texture(tex, uv); + if (tex_val != vec4(0.0,0.0,0.0,0.0)) + FragColor = tex_val; + else + FragColor = blur_val; +} + +")"; +#endif \ No newline at end of file diff --git a/include/blt/gfx/shader.h b/include/blt/gfx/shader.h index cb2a383..e48f32a 100644 --- a/include/blt/gfx/shader.h +++ b/include/blt/gfx/shader.h @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include namespace blt::gfx { @@ -32,99 +35,103 @@ namespace blt::gfx GLuint uboID = 0; size_t size_; GLuint location_; - + public: explicit uniform_buffer(size_t size, GLuint location = 0); - + uniform_buffer(void* data, size_t size, GLuint location = 0); - + /** * Resizes the internal UBO * @param newSize new size for the UBO */ uniform_buffer& resize(size_t newSize); - + /** * Uploads data to the UBO. This can be an arbitrary locations and does not need to be the whole UBO. */ uniform_buffer& upload(void* data, size_t size, size_t offset = 0); - + uniform_buffer& bind(); - + inline uniform_buffer& unbind() { glBindBuffer(GL_UNIFORM_BUFFER, 0); return *this; } - + ~uniform_buffer(); - - [[nodiscard]] inline size_t size() const { return size_; } - - [[nodiscard]] inline GLuint location() const { return location_; } + + [[nodiscard]] inline size_t size() const + { return size_; } + + [[nodiscard]] inline GLuint location() const + { return location_; } }; - + class shader_base_t { - friend uniform_buffer; - + friend uniform_buffer; + protected: struct IntDefaultedToMinusOne { GLint i = -1; - - inline explicit operator bool() const { return i != -1; } + + inline explicit operator bool() const + { return i != -1; } }; - + std::unordered_map uniformVars; GLuint programID = 0; - + IntDefaultedToMinusOne getUniformLocation(const std::string& name); - + public: const shader_base_t& bind() const { glUseProgram(programID); return *this; } - + shader_base_t& bind() { glUseProgram(programID); return *this; } - + shader_base_t& unbind() { glUseProgram(0); return *this; } - + shader_base_t& setBool(const std::string& name, bool value); - + shader_base_t& setInt(const std::string& name, int value); - + shader_base_t& setFloat(const std::string& name, float value); - + shader_base_t& setMatrix(const std::string& name, blt::mat4x4& matrix); - + // poor solution: TODO shader_base_t& setMatrix(const std::string& name, blt::mat4x4&& matrix); - + shader_base_t& setVec2(const std::string& name, const blt::vec2& vec); - + shader_base_t& setVec3(const std::string& name, const blt::vec3& vec); - + shader_base_t& setVec4(const std::string& name, const blt::vec4& vec); + shader_base_t& setVec4i(const std::string& name, const blt::vec4i& vec); - + shader_base_t& setVec2(const std::string& name, float x, float y); - + shader_base_t& setVec3(const std::string& name, float x, float y, float z); - + shader_base_t& setVec4(const std::string& name, float x, float y, float z, float w); }; - + /** * a basic computer shader class, contains the functions and resources required to use compute shaders! */ @@ -132,47 +139,87 @@ namespace blt::gfx { private: GLuint shaderID = 0; - + public: explicit compute_shader_t(const std::string& shader_source, bool loadAsString = true); - + void execute(const int x, const int y, const int z) const { bind(); glDispatchCompute(x, y, z); } - + ~compute_shader_t(); }; - + class shader_t : public shader_base_t { private: GLuint vertexShaderID = 0; GLuint fragmentShaderID = 0; - + static unsigned int createShader(const std::string& source, int type); - + static std::string loadShader(std::string_view file); - + + shader_t(std::string_view vertex, std::string_view fragment); + + static std::pair process_templates(template_engine_t& engine, std::string_view vertex, + std::string_view fragment); + public: /** - * Creates a shader - * @param vertex vertex shader source or file - * @param fragment fragment shader source or file - * @param geometry geometry shader source or file (optional) - * @param load_as_string load the shader as a string (true) or use the string to load the shader as a file (false) + * Creates a shader from source file + * @param vertex vertex shader source + * @param fragment fragment shader source */ - shader_t(const std::string& vertex, const std::string& fragment, bool load_as_string = true); - + static shader_t* make(std::optional> engine, std::string_view vertex, + std::string_view fragment); + + static shader_t* make(std::string_view vertex, std::string_view fragment) + { + return make({}, vertex, fragment); + } + + static std::unique_ptr make_unique(std::optional> engine, + std::string_view vertex, std::string_view fragment); + + static std::unique_ptr make_unique(std::string_view vertex, std::string_view fragment) + { + return make_unique({}, vertex, fragment); + } + + /** + * Creates a shader by loading lines of source from the file. + * @param vertex vertex shader file path + * @param fragment fragment shader file path + */ + static shader_t* load(std::optional> engine, std::string_view vertex, + std::string_view fragment); + + static shader_t* load(std::string_view vertex, std::string_view fragment) + { + return load({}, vertex, fragment); + } + + static std::unique_ptr load_unique(std::optional> engine, + std::string_view vertex, std::string_view fragment); + + static std::unique_ptr load_unique(std::string_view vertex, std::string_view fragment) + { + return load_unique({}, vertex, fragment); + } + shader_t(shader_t&& move) noexcept; - + + shader_t(const shader_t& copy) = delete; + // used to set the location of VAOs to the in variables in opengl shaders. void bindAttribute(int attribute, const std::string& name) const; - + // used to set location of shared UBOs like the perspective and view matrix void setUniformBlockLocation(const std::string& name, int location) const; - + ~shader_t(); }; } diff --git a/libraries/BLT b/libraries/BLT index fa5083b..f228cfb 160000 --- a/libraries/BLT +++ b/libraries/BLT @@ -1 +1 @@ -Subproject commit fa5083b637a96af5b430c9f0015ee11d5638d201 +Subproject commit f228cfbbe31538731a2ef1bd990d41c99562c4d3 diff --git a/libraries/imgui b/libraries/imgui index a1b0682..231cbee 160000 --- a/libraries/imgui +++ b/libraries/imgui @@ -1 +1 @@ -Subproject commit a1b06823fe2d964a62fda99385499b218cf5cea5 +Subproject commit 231cbee0fc4f59dbe5b8b853a11b08dc84e57c65 diff --git a/libraries/openal-soft b/libraries/openal-soft index 6675317..111397c 160000 --- a/libraries/openal-soft +++ b/libraries/openal-soft @@ -1 +1 @@ -Subproject commit 6675317107257c2cc16c947b359d557821d85bf2 +Subproject commit 111397c71a5f1c2c88e05da9c84edfdba2e472a4 diff --git a/src/blt/gfx/renderer/batch_2d_renderer.cpp b/src/blt/gfx/renderer/batch_2d_renderer.cpp index 1a3173f..e11effa 100644 --- a/src/blt/gfx/renderer/batch_2d_renderer.cpp +++ b/src/blt/gfx/renderer/batch_2d_renderer.cpp @@ -81,11 +81,11 @@ namespace blt::gfx line_vao->bindElement(indices_vbo); } - square_shader = new shader_t(shader_2d_textured_vert, shader_2d_textured_frag); + square_shader = shader_t::make(shader_2d_textured_vert, shader_2d_textured_frag); square_shader->bindAttribute(0, "vertex"); square_shader->bindAttribute(1, "uv_in"); - point_shader = new shader_t(shader_2d_textured_vert, shader_2d_textured_cirlce_frag); + point_shader = shader_t::make(shader_2d_textured_vert, shader_2d_textured_cirlce_frag); point_shader->bindAttribute(0, "vertex"); point_shader->bindAttribute(1, "uv_in"); diff --git a/src/blt/gfx/renderer/postprocess.cpp b/src/blt/gfx/renderer/postprocess.cpp index b060229..8095f58 100644 --- a/src/blt/gfx/renderer/postprocess.cpp +++ b/src/blt/gfx/renderer/postprocess.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -117,9 +118,18 @@ namespace blt::gfx steps.clear(); } - std::unique_ptr pp_engine_t::createShader(std::string_view fragment) + std::unique_ptr pp_engine_t::createShader(std::string_view fragment, std::optional> engine) { - auto shader = std::make_unique(shader_postprocess_vert, std::string(fragment)); + std::unique_ptr shader; + if (engine) + { + shader = shader_t::make_unique(engine, shader_postprocess_vert, std::string(fragment)); + } else + { + template_engine_t templateEngine; + templateEngine.set("LAYOUT_STRING", ""); + shader = shader_t::make_unique(templateEngine, shader_postprocess_vert, std::string(fragment)); + } shader->bindAttribute(0, "vertex"); shader->bindAttribute(1, "uv_in"); return shader; @@ -139,7 +149,10 @@ namespace blt::gfx void pp_in_place_t::create() { - shader_pass = pp_engine_t::createShader(shader_pp_screen_frag); + template_engine_t engine; + engine.set("LAYOUT_STRING", "layout (location = ${IF(LAYOUT_LOCATION) { LAYOUT_LOCATION } ELSE { ~DISCARD }}) "); + engine.set("LAYOUT_LOCATION", std::to_string(static_cast(to) - static_cast(frame_buffer_t::attachment_t::COLOR0))); + shader_pass = pp_engine_t::createShader(shader_pp_screen_frag, engine); draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight()); } @@ -274,4 +287,18 @@ namespace blt::gfx shader->bind(); shader->setVec4("multiplier", multiplier); } + + void pp_overlay_blur_step_t::create() + { + pp_in_place_t::create(); + shader = pp_engine_t::createShader(shader_overlay_blur_frag); + } + + void pp_overlay_blur_step_t::draw(frame_buffer_t& previous) + { + pp_in_place_t::draw(previous); + shader->bind(); + auto v = vec2(getWindowWidth(), getWindowHeight()); + shader->setVec4i("size", {static_cast(v.x()), static_cast(v.y()), x_blur, y_blur}); + } } \ No newline at end of file diff --git a/src/blt/gfx/shader.cpp b/src/blt/gfx/shader.cpp index e7dfd90..3f83ee2 100644 --- a/src/blt/gfx/shader.cpp +++ b/src/blt/gfx/shader.cpp @@ -70,13 +70,13 @@ namespace blt::gfx glDeleteBuffers(1, &uboID); } - static std::string removeEmptyFirstLines(const std::string& string) + static std::string removeEmptyFirstLines(std::string_view string) { - auto lines = blt::string::split(string, "\n"); + auto lines = blt::string::split_sv(string, "\n"); std::string new_source_string; for (const auto& line : lines) { - if (!line.empty() && !blt::string::contains(line, "\"")) + if (!blt::string::trim(line).empty() && !blt::string::starts_with(line, "\"")) { new_source_string += line; new_source_string += "\n"; @@ -122,21 +122,11 @@ namespace blt::gfx return shaderID; } - shader_t::shader_t(const std::string& vertex, const std::string& fragment, bool load_as_string) + shader_t::shader_t(std::string_view vertex, std::string_view fragment) { -// load shader sources - std::string vertex_source = vertex; - std::string fragment_source = fragment; - if (!load_as_string) - { - // BLT provides a recursive file loader for glsl shaders. It's pretty much just a recursive function looking for include statements. - vertex_source = loadShader(vertex); - fragment_source = loadShader(fragment); - } else - { - vertex_source = removeEmptyFirstLines(vertex_source); - fragment_source = removeEmptyFirstLines(fragment_source); - } + // load shader sources + std::string vertex_source = removeEmptyFirstLines(vertex); + std::string fragment_source = removeEmptyFirstLines(fragment); // create the shaders vertexShaderID = createShader(vertex_source, GL_VERTEX_SHADER); @@ -232,6 +222,64 @@ namespace blt::gfx return str; } + shader_t* shader_t::make(std::optional> engine, std::string_view vertex, + std::string_view fragment) + { + std::string vertex_source; + std::string fragment_source; + if (engine) + { + auto s_pair = process_templates(engine->get(), vertex, fragment); + vertex_source = std::move(s_pair.first); + fragment_source = std::move(s_pair.second); + } else + { + vertex_source = vertex; + fragment_source = fragment; + } + + return new shader_t(vertex_source, fragment_source); + } + + shader_t* shader_t::load(std::optional> engine, std::string_view vertex, + std::string_view fragment) + { + auto vertex_source = loadShader(vertex); + auto fragment_source = loadShader((fragment)); + return make(engine, vertex_source, fragment_source); + } + + std::pair shader_t::process_templates(template_engine_t& engine, std::string_view vertex, std::string_view fragment) + { + auto vertex_e = engine.evaluate(vertex); + auto fragment_e = engine.evaluate(fragment); + if (!vertex_e) + { + BLT_ERROR("Error vertex function returned: %d", static_cast(vertex_e.error())); + throw std::runtime_error("Unable to substitute vertex source"); + } + if (!fragment_e) + { + BLT_ERROR("Error fragment function returned: %d", static_cast(vertex_e.error())); + throw std::runtime_error("Unable to substitute fragment source"); + } + return {vertex_e.value(), fragment_e.value()}; + } + + std::unique_ptr shader_t::make_unique(std::optional> engine, std::string_view vertex, + std::string_view fragment) + { + return std::unique_ptr(shader_t::make(engine, vertex, fragment)); + } + + std::unique_ptr shader_t::load_unique(std::optional> engine, std::string_view vertex, + std::string_view fragment) + { + auto vertex_source = loadShader(vertex); + auto fragment_source = loadShader((fragment)); + return make_unique(engine, vertex_source, fragment_source); + } + shader_base_t& shader_base_t::setBool(const std::string& name, bool value) { if (auto i = getUniformLocation(name)) diff --git a/src/blt/gfx/window.cpp b/src/blt/gfx/window.cpp index 80712f7..a9e9ecb 100644 --- a/src/blt/gfx/window.cpp +++ b/src/blt/gfx/window.cpp @@ -251,9 +251,11 @@ namespace blt::gfx glfwSwapInterval(data.sync_interval); gladLoadGL(glfwGetProcAddress); #endif - + +#ifndef __EMSCRIPTEN__ glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(gl_error_callback, nullptr); +#endif /* -- Set up our local callbacks, ImGUI will then call these -- */ create_callbacks();