From 2cb665cf192dfd991e17fa5f4cb1d8496f139bd8 Mon Sep 17 00:00:00 2001 From: NaifBanana Date: Wed, 5 Feb 2025 01:21:48 -0600 Subject: [PATCH] Trying again omfg --- include/shader.h | 52 ++++++- main.cpp | 9 +- shader.cpp | 389 ++++++++++++++++++++++++++++++++++++++++++++--- shaders/vert.vs | 2 + 4 files changed, 428 insertions(+), 24 deletions(-) diff --git a/include/shader.h b/include/shader.h index 2434f03..66e422d 100644 --- a/include/shader.h +++ b/include/shader.h @@ -4,17 +4,41 @@ #include -#include -#include -#include +#include #include #include #include +#include #include #include +#include +#include + +#include +#include +#include namespace NB { +class ShaderPreprocessorError : public std::runtime_error { +public: + enum Codes : unsigned char { + NONE, + FILE_NOT_FOUND, + BUILTIN_NOT_FOUND, + CUSTOM, + UNDEFINED + }; + + const Codes code; + + ShaderPreprocessorError(const std::string&, const std::string& file="", int line=-1); + ShaderPreprocessorError(Codes, const std::string& arg="", const std::string& file="", int line=-1); + +protected: + static std::string errorCodeParser(Codes, const std::string& arg=""); +}; + class ShaderError : public std::runtime_error { public: ShaderError(const std::string&, const char* shad=nullptr, const std::string& file="", int line=-1); @@ -23,9 +47,28 @@ protected: std::string formatString(const std::string&, const char* shad=nullptr, const std::string& file="", int line=-1); }; +class ShaderPreprocessor { +public: + typedef ShaderPreprocessorError::Codes Codes; + + std::vector AcceptedExtensions = {"frag", "vert", "tess", "geom", "comp", "shad", "glsl"}; + std::vector Directories; + std::map BuiltIns; + + ShaderPreprocessor(); + + std::stringstream load(const std::string&) const; + std::stringstream include(const std::string&, const std::string& base="", bool relativeFirst=false) const; + std::stringstream loadFromRelative(const std::string&, const std::string& base="") const; + std::stringstream loadFromDirectory(const std::string&) const; + std::stringstream loadFromBuiltIn(const std::string&) const; + virtual std::stringstream process(std::stringstream&) const; + virtual std::stringstream process(std::string) const; +}; + class Shader { public: - Shader(const char* vertexPath, const char* fragmentPath); + Shader(const std::stringstream&, const std::stringstream&); Shader(); Shader(const Shader&); Shader& operator=(const Shader&); @@ -38,6 +81,7 @@ public: unsigned int id; private: + }; } diff --git a/main.cpp b/main.cpp index 53f0e1e..f26d974 100644 --- a/main.cpp +++ b/main.cpp @@ -27,7 +27,12 @@ int main() { glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - NB::Shader myShader("../shaders/vert.vs", "../shaders/frag.fs"); + NB::ShaderPreprocessor myPre; + myPre.Directories.push_back("../shaders/"); + + std::cout << myPre.process(std::string("vert.vs")).rdbuf(); + + /* NB::Shader myShader(myPre.load("vert.vs"), myPre.load("frag.fs")); myShader.use(); NB::DrawBuffer myCube(myShader, {{3, GL_FLOAT}, {3, GL_FLOAT}}, GL_TRIANGLES); @@ -78,7 +83,7 @@ int main() { // Events and buffer swap glfwPollEvents(); glfwSwapBuffers(window); - } + } */ glfwTerminate(); diff --git a/shader.cpp b/shader.cpp index fcd5d12..ae791ab 100644 --- a/shader.cpp +++ b/shader.cpp @@ -2,6 +2,36 @@ namespace NB{ +// ShaderPreprocessorError class +ShaderPreprocessorError::ShaderPreprocessorError( + const std::string& msg, + const std::string& file, + int line +) : code(Codes::UNDEFINED), std::runtime_error(formatDebugString(msg, file, line)) {} + +ShaderPreprocessorError::ShaderPreprocessorError( + Codes err_code, + const std::string& arg, + const std::string& file, + int line +) : code(err_code), std::runtime_error(formatDebugString(errorCodeParser(err_code, arg), file, line)) {} + +std::string ShaderPreprocessorError::errorCodeParser(Codes err_code, const std::string& arg) { + switch(err_code) { + case Codes::FILE_NOT_FOUND: + return "File '" + arg + "' not found."; + case Codes::BUILTIN_NOT_FOUND: + return "Built-in '" + arg + "' not found."; + case Codes::CUSTOM: + case Codes::UNDEFINED: + return arg; + case Codes::NONE: + default: + return ""; + } +} + +// ShaderError class ShaderError::ShaderError( const std::string& msg, const char* shad, @@ -30,26 +60,349 @@ std::string ShaderError::formatString( return ret; } -Shader::Shader(const char* vertexPath, const char* fragmentPath) { - std::string vertexCode, fragmentCode; - std::ifstream vShaderFile, fShaderFile; +// ShaderPreprocessor class +ShaderPreprocessor::ShaderPreprocessor() {} - vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - - try { - vShaderFile.open(vertexPath); - fShaderFile.open(fragmentPath); - std::stringstream vShaderStream, fShaderStream; - vShaderStream << vShaderFile.rdbuf(); - fShaderStream << fShaderFile.rdbuf(); - vShaderFile.close(); - fShaderFile.close(); - vertexCode = vShaderStream.str(); - fragmentCode = fShaderStream.str(); - } catch (std::ifstream::failure e) { - throw ShaderError("Could not read Shader files."); +std::stringstream ShaderPreprocessor::loadFromRelative(const std::string& path, const std::string& base) const { + std::string fullpath = base + path; + std::ifstream filestream; + std::stringstream ret; + filestream.open(fullpath); + if (filestream.is_open()) { + ret << filestream.rdbuf(); + return ret; } + filestream.close(); + for (const std::string& ext : AcceptedExtensions) { + filestream.open(fullpath + ext); + if (filestream.is_open()) { + ret << filestream.rdbuf(); + return ret; + } + filestream.close(); + } + throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, base + path); +} + +std::stringstream ShaderPreprocessor::loadFromDirectory(const std::string& name) const { + std::string fullpath; + std::ifstream filestream; + std::stringstream ret; + for (const std::string& path : Directories) { + fullpath = path + name; + filestream.open(fullpath); + if (filestream.is_open()) { + ret << filestream.rdbuf(); + return ret; + } + filestream.close(); + for (const std::string& ext : AcceptedExtensions) { + filestream.open(fullpath + ext); + if (filestream.is_open()) { + ret << filestream.rdbuf(); + } + filestream.close(); + } + } + throw ShaderPreprocessorError(Codes::FILE_NOT_FOUND, name); +} + +std::stringstream ShaderPreprocessor::loadFromBuiltIn(const std::string& name) const { + std::stringstream ret; + decltype(BuiltIns)::const_iterator builtin_it = BuiltIns.find(name); + if (builtin_it != BuiltIns.end()) { + ret << builtin_it->second; + return ret; + } + throw ShaderPreprocessorError(Codes::BUILTIN_NOT_FOUND, name); +} + +std::stringstream ShaderPreprocessor::load(const std::string& path) const { + try { + return loadFromRelative(path); + } catch (ShaderPreprocessorError e) { + if (e.code == Codes::FILE_NOT_FOUND) { + return loadFromDirectory(path); + } else { + throw e; + } + } +} + +std::stringstream ShaderPreprocessor::include(const std::string& name, const std::string& base, bool relativeFirst) const { + if (relativeFirst) { + try { + return loadFromRelative(name, base); + } catch (ShaderPreprocessorError e) { + if (e.code == Codes::FILE_NOT_FOUND) { + try { + return loadFromDirectory(name); + } catch (ShaderPreprocessorError f) { + if (f.code == Codes::FILE_NOT_FOUND) { + return loadFromBuiltIn(name); + } else { + throw f; + } + } + } else { + throw e; + } + } + } + try { + return loadFromDirectory(name); + } catch (ShaderPreprocessorError e) { + if (e.code == Codes::FILE_NOT_FOUND) { + try { + return loadFromBuiltIn(name); + } catch (ShaderPreprocessorError f) { + if (f.code == Codes::BUILTIN_NOT_FOUND) { + return loadFromRelative(name, base); + } else { + throw f; + } + } + } else { + throw e; + } + } +} + +std::stringstream ShaderPreprocessor::process(std::stringstream& code) const { +/* std::stringstream ret; + std::string line; + enum State : unsigned char { + Start, + Token, + PreProcToken, + Number, + WhiteSpace, + FSlash, + Operator, + BlockComment, + LineComment + }; + std::pair const NewState = {"", Start}; + std::vector> tokens({NewState}); + State currState; + char n; + for (char c; code.good() && !code.eof(); code.get(c)) { + currState = tokens.back().second; + if (std::isblank(c) && currState != BlockComment && currState != LineComment) { + if (c == '\n') { + + } + switch (currState) { + case WhiteSpace: + tokens.back().first += c; + continue; + break; + case Start: + tokens.back() = {"" + c, WhiteSpace}; + continue; + break; + default: + tokens.push_back({"" + c, WhiteSpace}); + continue; + break; + } + } + switch (currState) { + case Start: + if (c == '_' || std::isalpha(c)) { + tokens.back() = {"" + c, Token}; + continue; + } else if (c == '#') { + tokens.back() = {"#", PreProcToken}; + continue; + } else if (std::isdigit(c)) { + tokens.back() = {"" + c, Number}; + continue; + } else if (c == '.') { + code.get(n); + if (std::isdigit(n)) { + tokens.back() = {"" + c + n, Number}; + continue; + } else { + code.unget(); + tokens.back() = {".", Operator}; + tokens.push_back(NewState); + continue; + } + } else { + tokens.back() = {"" + c, Operator}; + tokens.push_back(NewState); + } + break; + case FSlash: + if (c == '*') { + tokens.back() = {"/*", BlockComment}; + } else if (c == '/') { + tokens.back() = {"//", LineComment}; + } else { + tokens.back().second = Operator; + } + continue; + break; + case BlockComment: + tokens.back().first += c; + code.get(n); + tokens.back().first += n; + if (c == '*' && n == '/') { + tokens.push_back(NewState); + } + break; + case LineComment: + tokens.back().first += c; + if (c == '\n') { + tokens.push_back(NewState); + } + break; + } + } + + return ret; */ + std::stringstream ret; + const std::vector PreprocessorKeywords = {"#include"}; + + enum State : unsigned char { + Symbol, + Directive, + BlockCommentStart, + BlockCommentEnd, + LinecommentStart, + Whitespace, + Pound, + SingleQuote, + DoubleQuote, + OpenAngle, + CloseAngle, + FSlash, + Semicolon, + Star, + Newline, + Operator + }; + std::vector> stack = {}; + for (char c; code.good() && !code.eof(); code.get(c)) { + State currstate = stack.back().first; + bool editstack = false; + switch (c) { + case '#': + stack.push_back({Pound, "#"}); + break; + case '"': + stack.push_back({DoubleQuote, "\""}); + break; + case '\'': + stack.push_back({SingleQuote, "'"}); + break; + case '<': + stack.push_back({OpenAngle, "<"}); + break; + case '>': + stack.push_back({CloseAngle, ">"}); + break; + case '/': + stack.push_back({FSlash, "/"}); + break; + case '*': + stack.push_back({Star, "*"}); + break; + case '\n': + stack.push_back({Newline, "\n"}); + break; + default: + if (isblank(c)) { + stack.push_back({Whitespace, std::string(1, c)}); + } else if (isalnum(c)) { + if (currstate == Symbol) { + stack.back().second += c; + } else { + stack.push_back({Symbol, std::string(1, c)}); + } + } else { + stack.push_back({Operator, std::string(1, c)}); + } + break; + } + + auto endIt = stack.end(); + State secondlast = endIt[-2].first; + switch (endIt[-1].first) { + case FSlash: + if (secondlast == FSlash) { + stack.erase(endIt-2, endIt); + stack.push_back({LinecommentStart, "//"}); + } else if (secondlast == Star) { + stack.erase(endIt-2, endIt); + stack.push_back({BlockCommentEnd, "*/"}); + } + break; + case Star: + if (secondlast == FSlash) { + stack.erase(endIt-2, endIt); + stack.push_back({BlockCommentStart, "/*"}); + } + break; + case Newline: + for (auto i : stack) { + + } + + } + } +} + +std::stringstream ShaderPreprocessor::process(std::stringstream& code) const { + std::stringstream ret; + std::string line; + const std::vector preprocKeys = { + "#include", + "//", + "/*", + "*/" + }; + const size_t keysListSize = preprocKeys.size(); + enum Context : uint8_t { + Code, + LineComment, + BlockComment + }; + Context currcontext = Code; + bool blockcomment = false; + std::vector keysIdx(keysListSize); + // Use linked list to count char len of each line + while(std::getline(code, line)) { + if (!blockcomment) { + for (int i = 0; i < keysListSize; ++i) { + keysIdx[i] = line.find(preprocKeys[i]); + } + } else if (keysIdx[2] == std::string::npos) { + ret << line; + } else { + + } + } + if (keysIdx[0] != std::string::npos) { + if (keysIdx[1] != std::string::npos) { + if ( (keysIdx[0] > keysIdx[1]) ) { + + } + } + } +} + +std::stringstream ShaderPreprocessor::process(std::string name) const { + std::stringstream code = load(name); + return process(code); +} + +// Shader class +Shader::Shader(const std::stringstream& vertexShader, const std::stringstream& fragmentShader) { + std::string vertexCode, fragmentCode; + vertexCode = vertexShader.str(); + fragmentCode = fragmentShader.str(); const char* vShaderCode = vertexCode.c_str(); const char* fShaderCode = fragmentCode.c_str(); diff --git a/shaders/vert.vs b/shaders/vert.vs index a55ca7a..2c74e96 100644 --- a/shaders/vert.vs +++ b/shaders/vert.vs @@ -2,6 +2,8 @@ layout (location=0) in vec3 vPos; layout (location=1) in vec3 vColor; +#include "frag.fs" + uniform mat4 look; out vec3 vertColor;