/* * Created by Brett on 08/02/23. * Licensed under GNU General Public License V3.0 * See LICENSE file for license detail */ #include std::vector blt::fs::getLinesFromFile(const std::string& path) { std::string shaderSource; std::ifstream shaderFile; if (!shaderFile.good()) BLT_ERROR("Input stream not good!\n"); // ensure ifstream objects can throw exceptions: shaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); try { // open file shaderFile.open(path); std::stringstream shaderStream; // read file's buffer contents into streams shaderStream << shaderFile.rdbuf(); // close file handlers shaderFile.close(); // convert stream into std::string shaderSource = shaderStream.str(); } catch (std::ifstream::failure& e) { BLT_WARN("Unable to read file '%s'!\n", path.c_str()); BLT_WARN("Exception: %s", e.what()); throw std::runtime_error("Failed to read file!\n"); } // split the shader into the lines, this way we can get out the #include statements. return string::split(shaderSource, "\n"); } std::vector blt::fs::recursiveShaderInclude(const std::string& path) { std::string pathOnly = path.substr(0, path.find_last_of('/')); auto mainLines = getLinesFromFile(path); std::unordered_map> includes; for (unsigned int i = 0; i < mainLines.size(); i++) { auto& line = mainLines[i]; // if the line is an include statement then we want to add lines recursively. if (string::starts_with(line, "#include")) { std::vector include_statement = string::split(line, "<"); if (include_statement.size() <= 1) include_statement = string::split(line, "\""); string::trim(line); if (!(string::ends_with(line, ">") || string::ends_with(line, "\""))) { BLT_FATAL("Shader file contains an invalid #include statement. (Missing terminator)\n"); std::abort(); } try { // filter out the > or " at the end of the include statement. std::string file; file = include_statement[1]; if (string::ends_with(include_statement[1], ">")) file = file.substr(0, file.size() - 1); BLT_TRACE("Recusing into %s/%s\n", pathOnly.c_str(), file.c_str()); includes.insert({i, recursiveShaderInclude((pathOnly + "/" + file))}); } catch (std::exception& e) { BLT_FATAL("Shader file contains an invalid #include statement. (Missing < or \")\n"); BLT_FATAL("Exception: %s", e.what()); std::abort(); } } } std::vector returnLines; // now combine all the loaded files while respecting the include's position in the file. for (unsigned int i = 0; i < mainLines.size(); i++) { if (!includes[i].empty()) { auto includedFileLines = includes[i]; for (const auto& line : includedFileLines) returnLines.push_back(line); } else returnLines.push_back(mainLines[i]); } return returnLines; }