Refactor of the shader class into a cleaner more functional graphics namespace and a procedural builder-style shader

This commit is contained in:
Ben Kyd
2023-04-23 14:10:02 +01:00
parent 5fd66cfdfa
commit b4e7a51400
7 changed files with 133 additions and 145 deletions

View File

@@ -23,9 +23,9 @@
#include <chrono>
#include <numeric>
namespace inferno::core {
using namespace inferno;
InfernoApp* create_inferno()
InfernoApp* inferno_create()
{
// MOTD
yolo::info("INFERNO HART v" INFERNO_VERSION);
@@ -38,11 +38,11 @@ InfernoApp* create_inferno()
return app;
}
void cleanup_inferno(InfernoApp* app)
void inferno_cleanup(InfernoApp* app)
{
}
static void gui_help_marker(const char* desc)
static void inferno_gui_help_marker(const char* desc)
{
ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort))
@@ -55,7 +55,7 @@ static void gui_help_marker(const char* desc)
}
}
void preset_gui(InfernoApp *app)
void inferno_preset_gui(InfernoApp *app)
{
ImGuiID dockspace_id = ImGui::GetID("main");
@@ -72,7 +72,7 @@ void preset_gui(InfernoApp *app)
yolo::info("LAYOUT SET TO DEFAULT");
}
void move_input(InfernoApp *app)
void inferno_move_input(InfernoApp *app)
{
static GLFWcursor* cursor = glfwCreateStandardCursor(GLFW_HAND_CURSOR);
glfwSetCursor(app->Win->getGLFWWindow(), cursor);
@@ -114,16 +114,14 @@ void move_input(InfernoApp *app)
app->Input->MovementDelta |= 0b00000100;
}
void stop_move_input(InfernoApp *app)
void inferno_stop_move_input(InfernoApp *app)
{
app->Input->MovementDelta = 0x0;
app->Input->MouseDelta = { 0.0f, 0.0f };
}
int run(InfernoApp *app)
int inferno_run(InfernoApp *app)
{
return 1;
}
}

View File

@@ -2,9 +2,9 @@
#include "graphics.hpp"
#include <singleton.hpp>
#include <memory>
namespace inferno::core {
namespace inferno {
class Window;
class HHM;
@@ -19,12 +19,12 @@ typedef struct InfernoApp {
InfernoInput* Input;
} InfernoApp;
InfernoApp* create_inferno();
void cleanup_inferno(InfernoApp* app);
void preset_gui(InfernoApp* app);
void move_input(InfernoApp* app);
void stop_move_input(InfernoApp* app);
int run(InfernoApp* app);
InfernoApp* inferno_create();
void inferno_cleanup(InfernoApp* app);
void inferno_preset_gui(InfernoApp* app);
void inferno_move_input(InfernoApp* app);
void inferno_stop_move_input(InfernoApp* app);
int inferno_run(InfernoApp* app);
}

View File

@@ -5,7 +5,7 @@
int main(int argc, char** argv)
{
auto* inferno = inferno::core::create_inferno();
return inferno::core::run(inferno);
auto* inferno = inferno::inferno_create();
return inferno::inferno_run(inferno);
}

View File

@@ -3,6 +3,14 @@
#include <iostream>
#include <fstream>
using namespace inferno::graphics;
static std::unordered_map<GLuint, int> shader2Index = {
{GL_VERTEX_SHADER, 0},
{GL_GEOMETRY_SHADER, 1},
{GL_FRAGMENT_SHADER, 2}
};
inline std::string trim(std::string& str)
{
str.erase(str.find_last_not_of(' ')+1); //suffixing spaces
@@ -10,126 +18,19 @@ inline std::string trim(std::string& str)
return str;
}
static std::unordered_map<GLuint, int> Shader2Index = {
{GL_VERTEX_SHADER, 0},
{GL_GEOMETRY_SHADER, 1},
{GL_FRAGMENT_SHADER, 2}
};
using namespace inferno;
std::string textFromFile(const std::filesystem::path& path) {
std::ifstream input(path);
return std::string((std::istreambuf_iterator<char>(input)),
std::istreambuf_iterator<char>());
}
Shader::Shader()
: mShaders({ GL_NONE, GL_NONE, GL_NONE})
, mProgram(0) {
std::vector<const ShaderPreprocessorDefinition*> getKeys(std::unique_ptr<Shader>& shader, std::string key) {
std::vector<const ShaderPreprocessorDefinition*> ret;
for (const auto& p : shader->PreprocessorDefinitions) if (p.key == key) ret.push_back(&p);
return ret;
}
Shader::~Shader() {
for (int i = 0; i < 3; i++) {
if (mShaders[i] == GL_NONE) continue;
glDeleteShader(mShaders[i]);
}
}
Shader* Shader::load(std::filesystem::path path) {
assert(std::filesystem::exists(path));
std::string loadedShaderSource = textFromFile(path);
for (int i = 0; i < loadedShaderSource.length(); i++) {
const char& c = loadedShaderSource[i];
if (c == '#') {
mPreprocessorDefinition def = { .start = i };
int j;
for (j = ++i; loadedShaderSource[j] != ' '; j++) {
def.key += loadedShaderSource[j];
}
for (j++; loadedShaderSource[j] != '\n'; j++) {
def.def += loadedShaderSource[j];
}
def.end = j; i = j; // advance i
def.def = trim(def.def);
def.key = trim(def.key);
mDefinitions.push_back(def);
}
}
// now we have all of the key/value definitions
// we can extract the relavent ones, for example
// "type"
auto types = mGetKeys("type");
int i = 0;
for (const mPreprocessorDefinition* type : types) {
GLuint glType = GL_NONE;
if (type->def == "vertex") glType = GL_VERTEX_SHADER;
if (type->def == "geometry") glType = GL_GEOMETRY_SHADER;
if (type->def == "fragment") glType = GL_FRAGMENT_SHADER;
assert(glType != GL_NONE);
mShaders[Shader2Index[glType]] = glCreateShader(glType);
const char* source = loadedShaderSource.c_str() + type->end;
int end = types.size() - 1 == i ? types.size(): types[i + 1]->start;
int length = end - type->end;
glShaderSource(mShaders[Shader2Index[glType]], 1, &source, &length);
i++;
}
return this;
}
Shader* Shader::link() {
mProgram = glCreateProgram();
for (int i = 0; i < 3; i++) {
if (mShaders[i] == GL_NONE) continue;
glCompileShader(mShaders[i]);
if (!mCheckShader(mShaders[i])) continue;
glAttachShader(mProgram, mShaders[i]);
}
glLinkProgram(mProgram);
return this;
}
Shader* Shader::use() {
glUseProgram(mProgram);
return this;
}
Shader* Shader::unUse() {
glUseProgram(0);
return this;
}
GLuint Shader::getProgram()
{
return mProgram;
}
void Shader::addAttribute(const std::string& attribute) {
mAttributes[attribute] = glGetAttribLocation(mProgram, attribute.c_str());
}
void Shader::addUniform(const std::string& uniform) {
mUniformLocations[uniform] = glGetUniformLocation(mProgram, uniform.c_str());
}
bool Shader::mCheckShader(GLuint uid) {
bool checkShader(GLuint uid) {
GLint isCompiled = 0;
glGetShaderiv(uid, GL_COMPILE_STATUS, &isCompiled);
if(isCompiled == GL_FALSE)
@@ -151,8 +52,112 @@ bool Shader::mCheckShader(GLuint uid) {
return true;
}
std::vector<const Shader::mPreprocessorDefinition*> Shader::mGetKeys(std::string key) {
std::vector<const Shader::mPreprocessorDefinition*> ret;
for (const auto& p : mDefinitions) if (p.key == key) ret.push_back(&p);
return ret;
std::unique_ptr<Shader> shader_create() {
std::unique_ptr<Shader> shader = std::make_unique<Shader>();
shader->Program = 0;
shader->Shaders[0] = GL_NONE;
shader->Shaders[1] = GL_NONE;
shader->Shaders[2] = GL_NONE;
return shader;
}
void shader_cleanup(std::unique_ptr<Shader>& shader) {
for (int i = 0; i < 3; i++) {
if (shader->Shaders[i] == GL_NONE) continue;
glDeleteShader(shader->Shaders[i]);
}
glDeleteProgram(shader->Program);
}
void shader_load(std::unique_ptr<Shader>& shader, std::filesystem::path path) {
assert(std::filesystem::exists(path));
std::string loadedShaderSource = textFromFile(path);
for (int i = 0; i < loadedShaderSource.length(); i++) {
const char& c = loadedShaderSource[i];
if (c == '#') {
ShaderPreprocessorDefinition def = { .start = i };
int j;
for (j = ++i; loadedShaderSource[j] != ' '; j++) {
def.key += loadedShaderSource[j];
}
for (j++; loadedShaderSource[j] != '\n'; j++) {
def.def += loadedShaderSource[j];
}
def.end = j; i = j; // advance i
def.def = trim(def.def);
def.key = trim(def.key);
shader->PreprocessorDefinitions.push_back(def);
}
}
// now we have all of the key/value definitions
// we can extract the relavent ones, for example
// "type"
std::vector<const ShaderPreprocessorDefinition*> types = getKeys(shader, "type");
int i = 0;
for (const ShaderPreprocessorDefinition* type : types) {
GLuint glType = GL_NONE;
if (type->def == "vertex") glType = GL_VERTEX_SHADER;
if (type->def == "geometry") glType = GL_GEOMETRY_SHADER;
if (type->def == "fragment") glType = GL_FRAGMENT_SHADER;
assert(glType != GL_NONE);
shader->Shaders[shader2Index[glType]] = glCreateShader(glType);
const char* source = loadedShaderSource.c_str() + type->end;
int end = types.size() - 1 == i ? types.size(): types[i + 1]->start;
int length = end - type->end;
glShaderSource(shader->Shaders[shader2Index[glType]], 1, &source, &length);
i++;
}
}
void shader_link(std::unique_ptr<Shader> &shader) {
shader->Program = glCreateProgram();
for (int i = 0; i < 3; i++) {
if (shader->Shaders[i] == GL_NONE) continue;
glCompileShader(shader->Shaders[i]);
if (!checkShader(shader->Shaders[i])) continue;
glAttachShader(shader->Program, shader->Shaders[i]);
}
glLinkProgram(shader->Program);
}
void shader_add_attribute(std::unique_ptr<Shader> &shader, const std::string &attribute) {
shader->Attributes[attribute] = glGetAttribLocation(shader->Program, attribute.c_str());
}
void shader_add_uniform(std::unique_ptr<Shader> &shader, const std::string &uniform) {
shader->Uniforms[uniform] = glGetUniformLocation(shader->Program, uniform.c_str());
}
GLuint shader_get_attribute(std::unique_ptr<Shader> &shader, const std::string &attribute) {
return shader->Attributes[attribute];
}
GLuint shader_get_uniform(std::unique_ptr<Shader> &shader, const std::string &uniform) {
return shader->Uniforms[uniform];
}
void shader_use(std::unique_ptr<Shader>& shader) {
glUseProgram(shader->Program);
}
void shader_unuse(std::unique_ptr<Shader>& shader) {
glUseProgram(0);
}

View File

@@ -8,52 +8,37 @@
#include <filesystem>
#include <unordered_map>
namespace inferno {
namespace inferno::graphics {
class Shader {
public:
Shader();
~Shader();
Shader* preprocessorDefine(std::string statement);
Shader* load(std::filesystem::path path);
Shader* link();
Shader* use();
Shader* unUse();
GLuint getProgram();
void addAttribute(const std::string& attribute);
void addUniform(const std::string& uniform);
GLuint operator[](const std::string& attribute) {
return mAttributes[attribute];
}
GLuint operator()(const std::string& uniform) {
return mUniformLocations[uniform];
}
private:
GLuint mShaders[3];
GLuint mProgram;
std::unordered_map<std::string, GLuint> mAttributes;
std::unordered_map<std::string, GLuint> mUniformLocations;
bool mCheckShader(GLuint uid);
private:
// preprocessor definitions can be defined by their
// start/end index, this is so that text can either be inserted
// in their place or for the "type" definition, the program
// can count until the next "type" definition per shader
struct mPreprocessorDefinition {
typedef struct ShaderPreprocessorDefinition {
int start, end;
std::string key;
std::string def;
};
std::vector<mPreprocessorDefinition> mDefinitions;
std::vector<const Shader::mPreprocessorDefinition*> mGetKeys(std::string key);
};
} shaderpreprocessordefinition;
typedef struct Shader {
GLuint Shaders[3];
GLuint Program;
std::unordered_map<std::string, GLuint> Attributes;
std::unordered_map<std::string, GLuint> Uniforms;
std::vector<ShaderPreprocessorDefinition> PreprocessorDefinitions;
} Shader;
std::unique_ptr<Shader> shader_create();
void shader_cleanup(std::unique_ptr<Shader>& shader);
void shader_load(std::unique_ptr<Shader>& shader, std::filesystem::path path);
void shader_link(std::unique_ptr<Shader>& shader);
// TODO: Implement shader_reload
void shader_add_attribute(std::unique_ptr<Shader>& shader, const std::string& attribute);
void shader_add_uniform(std::unique_ptr<Shader>& shader, const std::string& uniform);
GLuint shader_get_attribute(std::unique_ptr<Shader>& shader, const std::string& attribute);
GLuint shader_get_uniform(std::unique_ptr<Shader>& shader, const std::string& uniform);
void shader_use(std::unique_ptr<Shader>& shader);
void shader_unuse(std::unique_ptr<Shader>& shader);
}

View File

@@ -4,7 +4,7 @@
#include "yolo/yolo.hpp"
using namespace inferno::core;
using namespace inferno;
Window::Window() {}

View File

@@ -7,7 +7,7 @@
#define WINDOW_FLAGS ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoCollapse
namespace inferno::core {
namespace inferno {
typedef void (*KeyCallback)(int key, int scan, int action, int mod);
typedef void (*MouseCallback)(double x, double y);