From a60cc70246a9bace2aa1e7e979bed679dd012a56 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 28 Nov 2023 16:13:49 +0300 Subject: [PATCH] GLSL 'include' support --- res/shaders/background.glslf | 2 - res/shaders/background.glslv | 2 - res/shaders/lib/commons.glsl | 11 +++ res/shaders/lines.glslf | 2 - res/shaders/lines.glslv | 2 - res/shaders/main.glslf | 2 - res/shaders/main.glslv | 11 +-- res/shaders/skybox_gen.glslf | 2 - res/shaders/skybox_gen.glslv | 2 - res/shaders/ui.glslf | 2 - res/shaders/ui.glslv | 2 - src/assets/AssetsLoader.cpp | 2 +- src/coders/GLSLExtension.cpp | 145 +++++++++++++++++++++++++++++++++++ src/coders/GLSLExtension.h | 33 ++++++++ src/engine.cpp | 3 + src/files/files.cpp | 4 + src/graphics/Shader.cpp | 15 +++- src/graphics/Shader.h | 7 +- src/util/stringutil.cpp | 20 ++++- src/util/stringutil.h | 4 + 20 files changed, 238 insertions(+), 35 deletions(-) create mode 100644 res/shaders/lib/commons.glsl create mode 100644 src/coders/GLSLExtension.cpp create mode 100644 src/coders/GLSLExtension.h diff --git a/res/shaders/background.glslf b/res/shaders/background.glslf index c494c564..466372c9 100644 --- a/res/shaders/background.glslf +++ b/res/shaders/background.glslf @@ -1,5 +1,3 @@ -#version 330 core - in vec3 v_coord; out vec4 f_color; diff --git a/res/shaders/background.glslv b/res/shaders/background.glslv index e2d461c2..1370e400 100644 --- a/res/shaders/background.glslv +++ b/res/shaders/background.glslv @@ -1,5 +1,3 @@ -#version 330 core - layout (location = 0) in vec2 v_position; out vec3 v_coord; diff --git a/res/shaders/lib/commons.glsl b/res/shaders/lib/commons.glsl new file mode 100644 index 00000000..291c4cd8 --- /dev/null +++ b/res/shaders/lib/commons.glsl @@ -0,0 +1,11 @@ +// Example of a GLSL library + +vec4 decompress_light(float compressed_light) { + vec4 result; + int compressed = floatBitsToInt(compressed_light); + result.r = ((compressed >> 24) & 0xFF) / 255.f; + result.g = ((compressed >> 16) & 0xFF) / 255.f; + result.b = ((compressed >> 8) & 0xFF) / 255.f; + result.a = (compressed & 0xFF) / 255.f; + return result; +} diff --git a/res/shaders/lines.glslf b/res/shaders/lines.glslf index 3b42c1ab..e5e415e8 100644 --- a/res/shaders/lines.glslf +++ b/res/shaders/lines.glslf @@ -1,5 +1,3 @@ -#version 330 core - in vec4 v_color; out vec4 f_color; diff --git a/res/shaders/lines.glslv b/res/shaders/lines.glslv index 588950d7..649f3f87 100644 --- a/res/shaders/lines.glslv +++ b/res/shaders/lines.glslv @@ -1,5 +1,3 @@ -#version 330 core - layout (location = 0) in vec3 a_position; layout (location = 1) in vec4 a_color; diff --git a/res/shaders/main.glslf b/res/shaders/main.glslf index 1cc3d8b7..ab561063 100644 --- a/res/shaders/main.glslf +++ b/res/shaders/main.glslf @@ -1,5 +1,3 @@ -#version 330 core - in vec4 a_color; in vec2 a_texCoord; in float a_distance; diff --git a/res/shaders/main.glslv b/res/shaders/main.glslv index 7f50bc1f..4327f8c8 100644 --- a/res/shaders/main.glslv +++ b/res/shaders/main.glslv @@ -1,4 +1,4 @@ -#version 330 core +#include layout (location = 0) in vec3 v_position; layout (location = 1) in vec2 v_texCoord; @@ -22,15 +22,6 @@ uniform float u_torchlightDistance; #define SKY_LIGHT_MUL 2.5 -vec4 decompress_light(float compressed_light) { - vec4 result; - int compressed = floatBitsToInt(compressed_light); - result.r = ((compressed >> 24) & 0xFF) / 255.f; - result.g = ((compressed >> 16) & 0xFF) / 255.f; - result.b = ((compressed >> 8) & 0xFF) / 255.f; - result.a = (compressed & 0xFF) / 255.f; - return result; -} void main(){ vec2 pos2d = (u_model * vec4(v_position, 1.0)).xz-u_cameraPos.xz; diff --git a/res/shaders/skybox_gen.glslf b/res/shaders/skybox_gen.glslf index d7886eec..b5be530e 100644 --- a/res/shaders/skybox_gen.glslf +++ b/res/shaders/skybox_gen.glslf @@ -22,8 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#version 330 core - // first, lets define some constants to use (planet radius, position, and scattering coefficients) #define PLANET_POS vec3(0.0) /* the position of the planet */ #define PLANET_RADIUS 6371e3 /* radius of the planet */ diff --git a/res/shaders/skybox_gen.glslv b/res/shaders/skybox_gen.glslv index 91124116..fdd0667c 100644 --- a/res/shaders/skybox_gen.glslv +++ b/res/shaders/skybox_gen.glslv @@ -1,5 +1,3 @@ -#version 330 core - layout (location = 0) in vec2 v_position; out vec2 v_coord; diff --git a/res/shaders/ui.glslf b/res/shaders/ui.glslf index ef51d8bf..2e189a81 100644 --- a/res/shaders/ui.glslf +++ b/res/shaders/ui.glslf @@ -1,5 +1,3 @@ -#version 330 core - in vec2 a_textureCoord; in vec4 a_color; out vec4 f_color; diff --git a/res/shaders/ui.glslv b/res/shaders/ui.glslv index 2b65d101..d567e05d 100644 --- a/res/shaders/ui.glslv +++ b/res/shaders/ui.glslv @@ -1,5 +1,3 @@ -#version 330 core - layout (location = 0) in vec2 v_position; layout (location = 1) in vec2 v_textureCoord; layout (location = 2) in vec4 v_color; diff --git a/src/assets/AssetsLoader.cpp b/src/assets/AssetsLoader.cpp index 7f0eefbb..6ea3de5b 100644 --- a/src/assets/AssetsLoader.cpp +++ b/src/assets/AssetsLoader.cpp @@ -48,7 +48,7 @@ bool AssetsLoader::loadNext() { #include "../graphics/Font.h" bool _load_shader(Assets* assets, const path& filename, const std::string& name) { - Shader* shader = load_shader(filename.string() + ".glslv", filename.string() + ".glslf"); + Shader* shader = Shader::loadShader(filename.string() + ".glslv", filename.string() + ".glslf"); if (shader == nullptr) { std::cerr << "failed to load shader '" << name << "'" << std::endl; return false; diff --git a/src/coders/GLSLExtension.cpp b/src/coders/GLSLExtension.cpp new file mode 100644 index 00000000..bec4eb07 --- /dev/null +++ b/src/coders/GLSLExtension.cpp @@ -0,0 +1,145 @@ +#include "GLSLExtension.h" +#include +#include +#include +#include "../util/stringutil.h" +#include "../typedefs.h" +#include "../files/files.h" + +using std::string; +using std::filesystem::path; +namespace fs = std::filesystem; + +path GLSLExtension::getHeaderPath(string name) { + return libFolder/path(name+".glsl"); +} + +void GLSLExtension::setLibFolder(path folder) { + this->libFolder = folder; +} + +void GLSLExtension::setVersion(string version) { + this->version = version; +} + +void GLSLExtension::loadHeader(string name) { + path file = getHeaderPath(name); + string source = files::read_string(file); + addHeader(name, source); +} + +void GLSLExtension::addHeader(string name, string source) { + headers[name] = source; +} + +void GLSLExtension::define(string name, string value) { + defines[name] = value; +} + +const string& GLSLExtension::getHeader(const string name) const { + auto found = headers.find(name); + if (found == headers.end()) { + throw std::runtime_error("no header '"+name+"' loaded"); + } + return found->second; +} + +const string GLSLExtension::getDefine(const string name) const { + auto found = defines.find(name); + if (found == defines.end()) { + return ""; + } + return found->second; +} + +bool GLSLExtension::hasDefine(const string name) const { + return defines.find(name) != defines.end(); +} + +bool GLSLExtension::hasHeader(const string name) const { + return headers.find(name) != headers.end(); +} + +void GLSLExtension::undefine(string name) { + if (hasDefine(name)) { + defines.erase(name); + } +} + +inline std::runtime_error parsing_error( + const path& file, + uint linenum, + const string message) { + return std::runtime_error("file "+file.string()+": "+message+ + " at line "+std::to_string(linenum)); +} + +inline void parsing_warning( + const path& file, + uint linenum, const + string message) { + std::cerr << "file "+file.string()+": warning: "+message+ + " at line "+std::to_string(linenum) << std::endl; +} + +inline void source_line(std::stringstream& ss, const path& file, uint linenum) { + ss << "#line " << linenum << " " << file << '\n'; +} + +const string GLSLExtension::process(const path file, const string& source) { + std::stringstream ss; + size_t pos = 0; + uint linenum = 1; + ss << "#version " << version << '\n'; + for (auto& entry : defines) { + ss << "#define " << entry.first << " " << entry.second << '\n'; + } + source_line(ss, file, linenum); + while (pos < source.length()) { + size_t endline = source.find('\n', pos); + if (endline == string::npos) { + endline = source.length(); + } + // parsing preprocessor directives + if (source[pos] == '#') { + string line = source.substr(pos+1, endline-pos); + util::trim(line); + // parsing 'include' directive + if (line.find("include") != string::npos) { + line = line.substr(7); + util::trim(line); + if (line.length() < 3) { + throw parsing_error(file, linenum, + "invalid 'include' syntax"); + } + if (line[0] != '<' || line[line.length()-1] != '>') { + throw parsing_error(file, linenum, + "expected '#include ' syntax"); + } + string name = line.substr(1, line.length()-2); + path hfile = getHeaderPath(name); + if (!hasHeader(name)) { + loadHeader(name); + } + source_line(ss, hfile, 1); + ss << getHeader(name) << '\n'; + pos = endline+1; + linenum++; + source_line(ss, file, linenum); + continue; + } + // removing extra 'include' directives + else if (line.find("version") != string::npos) { + parsing_warning(file, linenum, "removed #version directive"); + pos = endline+1; + linenum++; + source_line(ss, file, linenum); + continue; + } + } + linenum++; + ss << source.substr(pos, endline+1-pos); + pos = endline+1; + } + return ss.str(); +} \ No newline at end of file diff --git a/src/coders/GLSLExtension.h b/src/coders/GLSLExtension.h new file mode 100644 index 00000000..74eb1ff8 --- /dev/null +++ b/src/coders/GLSLExtension.h @@ -0,0 +1,33 @@ +#ifndef CODERS_GLSL_EXTESION_H_ +#define CODERS_GLSL_EXTESION_H_ + +#include +#include +#include + +class GLSLExtension { + std::unordered_map headers; + std::unordered_map defines; + std::filesystem::path libFolder; + std::string version = "330 core"; + + void loadHeader(std::string name); + std::filesystem::path getHeaderPath(std::string name); +public: + void setLibFolder(std::filesystem::path folder); + void setVersion(std::string version); + + void define(std::string name, std::string value); + void undefine(std::string name); + void addHeader(std::string name, std::string source); + + const std::string& getHeader(const std::string name) const; + const std::string getDefine(const std::string name) const; + + bool hasHeader(const std::string name) const; + bool hasDefine(const std::string name) const; + + const std::string process(const std::filesystem::path file, const std::string& source); +}; + +#endif // CODERS_GLSL_EXTESION_H_ \ No newline at end of file diff --git a/src/engine.cpp b/src/engine.cpp index eae699e2..db46ccd5 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -15,6 +15,7 @@ #include "window/Camera.h" #include "window/input.h" #include "graphics/Batch2D.h" +#include "graphics/Shader.h" #include "graphics/ImageData.h" #include "frontend/gui/GUI.h" #include "frontend/screens.h" @@ -22,6 +23,7 @@ #include "coders/json.h" #include "coders/png.h" +#include "coders/GLSLExtension.h" #include "files/files.h" #include "files/engine_paths.h" @@ -35,6 +37,7 @@ using gui::GUI; Engine::Engine(EngineSettings& settings, EnginePaths* paths, Content* content) : settings(settings), content(content), paths(paths) { Window::initialize(settings.display); + Shader::preprocessor->setLibFolder(paths->getResources()/path("shaders/lib")); assets = new Assets(); std::cout << "-- loading assets" << std::endl; diff --git a/src/files/files.cpp b/src/files/files.cpp index a6b8fa72..b631778e 100644 --- a/src/files/files.cpp +++ b/src/files/files.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using std::ios; using std::string; @@ -57,6 +58,9 @@ char* files::read_bytes(path filename, size_t& length) { std::string files::read_string(path filename) { size_t size; unique_ptr chars (read_bytes(filename, size)); + if (chars == nullptr) { + throw std::runtime_error("could not to load file '"+filename.string()+"'"); + } return string(chars.get(), size); } diff --git a/src/graphics/Shader.cpp b/src/graphics/Shader.cpp index 7d28344d..ccc4df55 100644 --- a/src/graphics/Shader.cpp +++ b/src/graphics/Shader.cpp @@ -10,6 +10,12 @@ #include #include +#include "../coders/GLSLExtension.h" + +using std::filesystem::path; + +GLSLExtension* Shader::preprocessor = new GLSLExtension(); + Shader::Shader(unsigned int id) : id(id){ } @@ -57,7 +63,7 @@ void Shader::uniform3f(std::string name, glm::vec3 xyz){ } -Shader* load_shader(std::string vertexFile, std::string fragmentFile) { +Shader* Shader::loadShader(std::string vertexFile, std::string fragmentFile) { // Reading Files std::string vertexCode; std::string fragmentCode; @@ -84,6 +90,9 @@ Shader* load_shader(std::string vertexFile, std::string fragmentFile) { std::cerr << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl; return nullptr; } + vertexCode = preprocessor->process(path(vertexFile), vertexCode); + fragmentCode = preprocessor->process(path(fragmentFile), fragmentCode); + const GLchar* vShaderCode = vertexCode.c_str(); const GLchar* fShaderCode = fragmentCode.c_str(); @@ -98,7 +107,7 @@ Shader* load_shader(std::string vertexFile, std::string fragmentFile) { glGetShaderiv(vertex, GL_COMPILE_STATUS, &success); if (!success){ glGetShaderInfoLog(vertex, 512, nullptr, infoLog); - std::cerr << "SHADER::VERTEX: compilation failed" << std::endl; + std::cerr << "SHADER::VERTEX: compilation failed: " << vertexFile << std::endl; std::cerr << infoLog << std::endl; return nullptr; } @@ -110,7 +119,7 @@ Shader* load_shader(std::string vertexFile, std::string fragmentFile) { glGetShaderiv(fragment, GL_COMPILE_STATUS, &success); if (!success){ glGetShaderInfoLog(fragment, 512, nullptr, infoLog); - std::cerr << "SHADER::FRAGMENT: compilation failed" << std::endl; + std::cerr << "SHADER::FRAGMENT: compilation failed: " << vertexFile << std::endl; std::cerr << infoLog << std::endl; return nullptr; } diff --git a/src/graphics/Shader.h b/src/graphics/Shader.h index c91dea5a..659d717f 100644 --- a/src/graphics/Shader.h +++ b/src/graphics/Shader.h @@ -4,8 +4,11 @@ #include #include +class GLSLExtension; + class Shader { public: + static GLSLExtension* preprocessor; unsigned int id; Shader(unsigned int id); @@ -19,8 +22,8 @@ public: void uniform2f(std::string name, glm::vec2 xy); void uniform3f(std::string name, float x, float y, float z); void uniform3f(std::string name, glm::vec3 xyz); + + static Shader* loadShader(std::string vertexFile, std::string fragmentFile); }; -extern Shader* load_shader(std::string vertexFile, std::string fragmentFile); - #endif /* GRAPHICS_SHADER_H_ */ diff --git a/src/util/stringutil.cpp b/src/util/stringutil.cpp index 01f8b75d..11096681 100644 --- a/src/util/stringutil.cpp +++ b/src/util/stringutil.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using std::vector; using std::string; @@ -149,4 +150,21 @@ bool util::is_valid_filename(std::wstring name) { } } return true; -} \ No newline at end of file +} + +void util::ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +} + +void util::rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + +void util::trim(std::string &s) { + rtrim(s); + ltrim(s); +} diff --git a/src/util/stringutil.h b/src/util/stringutil.h index 6138455e..b1db1fe8 100644 --- a/src/util/stringutil.h +++ b/src/util/stringutil.h @@ -15,6 +15,10 @@ namespace util { extern bool is_integer(std::string text); extern bool is_integer(std::wstring text); extern bool is_valid_filename(std::wstring name); + + extern void ltrim(std::string &s); + extern void rtrim(std::string &s); + extern void trim(std::string &s); } #endif // UTIL_STRINGUTIL_H_ \ No newline at end of file