nuclear-plant-sim/src/graphics/shader/compile.cpp

144 lines
3.3 KiB
C++

#include "compile.hpp"
#include <filesystem>
#include <format>
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <GL/glew.h>
static constexpr std::string KEYWORD = "#include";
enum include_mode_t {
WHITESPACE,
READING,
ENDING,
};
static bool parse(std::vector<char>& source, const std::filesystem::path& path) {
std::ifstream file(path);
std::vector<char> line;
int n_line = 1;
int n_col = 0;
int keyword_at = 0;
include_mode_t mode = include_mode_t::WHITESPACE;
if(!file.is_open()) {
return false;
}
for(auto it = std::istreambuf_iterator<char>(file); it != std::istreambuf_iterator<char>(); it++) {
char c = *it;
if(c == '\0') {
break;
}
if(c == '\n') {
n_line++;
n_col = 0;
} else {
n_col++;
}
if(keyword_at < KEYWORD.size()) {
if(keyword_at == n_col - 1 && KEYWORD[keyword_at] == c) {
keyword_at++;
continue;
}
else if(keyword_at > 0) {
source.insert(source.end(), &KEYWORD[0], &KEYWORD[keyword_at]);
keyword_at = 0;
}
source.push_back(c);
continue;
}
switch(mode) {
case include_mode_t::WHITESPACE: {
if(c != ' ' && c != '\t' && c != '\r' && c != '"') {
throw std::runtime_error(std::format("At {}:{}: Syntax error", n_line, n_col));
}
if(c == '"') {
mode = include_mode_t::READING;
}
break;
}
case include_mode_t::READING: {
if(c != '"') {
line.push_back(c);
break;
}
mode = include_mode_t::ENDING;
std::filesystem::path path_next = path.parent_path().append(line.begin(), line.end());
std::string comment = std::format("//'{}' {}\n", path_next.string(), '{');
source.insert(source.end(), comment.begin(), comment.end());
if(!parse(source, path_next)) {
throw std::runtime_error(std::format("At {}:{} in '{}': File '{}' not found", n_line, n_col, path.string(), path_next.string()));
}
comment = "//}\n";
source.insert(source.end(), comment.begin(), comment.end());
line.clear();
break;
}
case include_mode_t::ENDING: {
if(c == '\r') {
break;
}
if(c != '\n') {
throw std::runtime_error(std::format("At {}:{} in '{}': Syntax error", n_line, n_col, path.string()));
}
mode = include_mode_t::WHITESPACE;
keyword_at = 0;
break;
}}
}
if(keyword_at > 0) {
source.insert(source.end(), &KEYWORD[0], &KEYWORD[keyword_at]);
}
return true;
}
void Graphics::Shader::compile(unsigned int shader, const std::filesystem::path& path) {
std::vector<char> source;
if(!parse(source, path)) {
throw std::runtime_error(std::format("File '{}' not found", path.string()));
}
const char* data = source.data();
int data_len = source.size();
glShaderSource(shader, 1, &data, &data_len);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if(!success) {
char info_log[1024];
glGetShaderInfoLog(shader, sizeof(info_log), nullptr, info_log);
std::ostringstream source_processed;
int n_line = 1;
source_processed << n_line << '\t';
for(char c : source) {
if(c == '\n') {
source_processed << '\n' << ++n_line << '\t';
} else {
source_processed << c;
}
}
throw std::runtime_error(std::format("OpenGL shader compilation error\n{}\nIn expanded source file '{}'\n{}", source_processed.str(), path.string(), info_log));
}
}