Browse Source

Add project and shader models, and (de)serialization of projects

master
gradient 8 years ago
parent
commit
937bdc530f
4 changed files with 302 additions and 0 deletions
  1. +49
    -0
      src/project.cpp
  2. +39
    -0
      src/project.h
  3. +162
    -0
      src/shaders.cpp
  4. +52
    -0
      src/shaders.h

+ 49
- 0
src/project.cpp View File

@ -0,0 +1,49 @@
#include "project.h"
#include <iostream>
std::unique_ptr<Project> Project::fromFile(std::string path) {
auto manifest = cpptoml::parse_file(path);
auto basepath = filesystem::path(path).parent_path().make_absolute();
return Project::fromToml(manifest, basepath);
}
std::unique_ptr<Project> Project::fromToml(std::shared_ptr<cpptoml::table> manifest,
filesystem::path basepath = filesystem::path(".")) {
auto project = Project();
project.name = manifest->get_qualified_as<std::string>("project.name").value_or("");
project.shader_bundle = std::move(std::make_shared<ShaderBundle>());
auto sceneconf = manifest->get_table_array("scene");
if (sceneconf) {
for (const auto& conf_scene : *sceneconf) {
auto scene = std::make_shared<Scene>();
scene->name = conf_scene->get_as<std::string>("name").value_or("");
auto p_frag = conf_scene->get_as<std::string>("frag");
auto p_vert = conf_scene->get_as<std::string>("vert");
if (p_frag && p_vert) {
std::vector<std::pair<std::string, ShaderBundle::ShaderType>> shaders;
shaders.push_back(std::make_pair((basepath / *p_vert).str(), GL_VERTEX_SHADER));
shaders.push_back(std::make_pair((basepath / *p_frag).str(), GL_FRAGMENT_SHADER));
scene->shader_p = project.shader_bundle->add_program(shaders);
}
project.scenes.push_back(std::move(scene));
}
}
auto boot_scene = manifest->get_qualified_as<int>("project.boot_scene");
if (boot_scene) {
printf("loading boot scene %i\n", *boot_scene);
project.load_scene(project.scenes[*boot_scene]);
project.shader_bundle->recompile().link();
}
return std::make_unique<Project>(project);
}
void Project::load_scene(std::shared_ptr<Scene> scene) {
this->current_scene = scene;
this->scene_loaded = true;
}

+ 39
- 0
src/project.h View File

@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <cpptoml.h>
#include "shaders.h"
#include "pipeline/post.h"
class Scene {
public:
std::string name;
ShaderBundle::ProgramHandle* shader_p;
PostConfig postConfig;
};
class NullScene : public Scene {
};
class Project {
public:
Project() = default;
static std::unique_ptr<Project> fromFile(std::string path);
static std::unique_ptr<Project> fromToml(std::shared_ptr<cpptoml::table> manifest, filesystem::path basepath);
void load_scene(std::shared_ptr<Scene> scene);
std::vector<std::shared_ptr<Scene>> scenes;
std::shared_ptr<Scene> current_scene;
bool scene_loaded = false;
std::string name;
std::shared_ptr<ShaderBundle> shader_bundle;
};

+ 162
- 0
src/shaders.cpp View File

@ -0,0 +1,162 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <cerrno>
#include <GL/gl3w.h>
#include <algorithm>
#include "shaders.h"
static std::string string_from_file(std::string path) {
std::ifstream in(path, std::ios::in);
if (!in) {
std::cerr << "Opening file " << path << " failed.\n";
}
std::ostringstream contents;
contents << in.rdbuf();
in.close();
return contents.str();
}
ShaderBundle::~ShaderBundle() {
for (std::pair<ShaderBundle::ShaderId, ShaderBundle::Shader> shader : this->shader_pool) {
glDeleteShader(shader.second.handle);
}
for (auto& program : this->programs) {
glDeleteProgram(program.second.internal_handle);
}
}
ShaderBundle::ProgramHandle* ShaderBundle::add_program(
const std::vector<std::pair<std::string, ShaderType>>& shaderPairs) {
std::vector<ShaderId> shader_ids;
for (auto shaderPair : shaderPairs) {
ShaderId shader_id;
std::tie(shader_id.name, shader_id.type) = shaderPair;
auto upserted = this->shader_pool.emplace(std::move(shader_id), Shader()).first;
// if the shader doesn't have a handle from OpenGL, get one
if (!upserted->second.handle) {
upserted->second.handle = glCreateShader(shaderPair.second);
upserted->second.hash =
(int32_t)std::hash<std::string>()(shaderPair.first) & 0x7FFFFFFF;
}
shader_ids.push_back(upserted->first);
}
std::sort(shader_ids.begin(), shader_ids.end());
shader_ids.erase(std::unique(shader_ids.begin(), shader_ids.end()), shader_ids.end());
auto upserted = this->programs.emplace(shader_ids, Program()).first;
if (!upserted->second.internal_handle) {
upserted->second.handle = 0;
upserted->second.internal_handle = glCreateProgram();
for (const auto shader : shader_ids) {
glAttachShader(upserted->second.internal_handle, this->shader_pool[shader].handle);
}
}
return &upserted->second.handle;
}
ShaderBundle& ShaderBundle::recompile() {
for (std::pair<const ShaderId, Shader> shader : this->shader_pool) {
auto source_s = string_from_file(shader.first.name);
source_s = ShaderBundle::preprocess(filesystem::path(shader.first.name), source_s);
const char *source_buf = source_s.c_str();
glShaderSource(shader.second.handle, 1, &source_buf, NULL);
glCompileShader(shader.second.handle);
GLint status;
glGetShaderiv(shader.second.handle, GL_COMPILE_STATUS, &status);
if (!status) {
GLint log_len;
glGetShaderiv(shader.second.handle, GL_INFO_LOG_LENGTH, &log_len);
std::vector<char> log(log_len + 1);
glGetShaderInfoLog(shader.second.handle, log_len, NULL, log.data());
std::string log_s = log.data();
std::cerr << "Error compiling: " << log_s << '\n';
}
std::cout << "Successfully compiled " << shader.first.name << '\n';
}
return *this;
}
ShaderBundle& ShaderBundle::link() {
for (std::pair<const std::vector<ShaderId>, Program>& program : this->programs) {
bool needs_relink = true;
/*for (const ShaderId p_shader : program.first) {
for (std::pair<const ShaderId, Shader> shader : this->shader_pool) {
if (shader.first == p_shader) {
needs_relink = true;
break;
}
}
if (needs_relink)
break;
}*/
// Don't relink programs that depend on shaders for which compilation failed
bool can_relink = true;
if (needs_relink) {
for (const ShaderId p_shader : program.first) {
GLint status;
glGetShaderiv(this->shader_pool[p_shader].handle, GL_COMPILE_STATUS, &status);
if (!status) {
can_relink = false;
break;
}
}
}
if (needs_relink && can_relink) {
glLinkProgram(program.second.internal_handle);
GLint status;
glGetProgramiv(program.second.internal_handle, GL_LINK_STATUS, &status);
if (!status) {
std::cerr << "Error linking shaders!\n";
program.second.handle = 0;
} else {
std::cout << "Successfully linked shaders\n";
}
program.second.handle = program.second.internal_handle;
}
}
return *this;
}
std::string ShaderBundle::preprocess(filesystem::path path, std::string shader) {
// TODO: this is probably not idiomatic C++; perhaps use a stringstream?
// TODO: proper erroring out.
std::string inc = "\n#include ";
auto directive_idx = shader.find(inc);
// if we didn't find an include directive, return the original shader.
if (directive_idx == std::string::npos)
return shader;
// skip to the end of the directive. read until \n to get the included file.
auto idx_end = directive_idx + inc.length();
auto idx_nl = shader.find('\n', idx_end);
auto file_str = shader.substr(idx_end + 1, idx_nl - idx_end - 2);
auto file_path = path.parent_path().make_absolute() / file_str;
auto included_glsl = preprocess(file_path, string_from_file(file_path.str()));
shader.replace(directive_idx+1, idx_nl - directive_idx - 1, included_glsl);
return preprocess(path, shader);
}

+ 52
- 0
src/shaders.h View File

@ -0,0 +1,52 @@
#pragma once
#include <GL/gl3w.h>
#include <vector>
#include <string>
#include <map>
#include <set>
#include "filesystem/path.h"
class ShaderBundle {
public:
using ShaderHandle = GLuint;
using ProgramHandle = GLuint;
using ShaderType = GLenum;
struct ShaderId {
std::string name;
ShaderType type;
bool operator<(const ShaderId& rhs) const {
return std::tie(name, type) < std::tie(rhs.name, rhs.type);
}
bool operator==(const ShaderId& rhs) const {
return std::tie(name, type) == std::tie(rhs.name, rhs.type);
}
};
struct Shader {
ShaderHandle handle;
int32_t hash;
};
struct Program {
// public handle; 0 until the program has successfully linked
ProgramHandle handle;
ProgramHandle internal_handle;
};
ShaderBundle() = default;
~ShaderBundle();
std::map<ShaderId, Shader> shader_pool;
std::map<std::vector<ShaderId>, Program> programs;
ProgramHandle* add_program(const std::vector<std::pair<std::string, ShaderType>>& shaders);
ShaderBundle& recompile();
ShaderBundle& link();
static std::string preprocess(filesystem::path path, std::string shader);
};

Loading…
Cancel
Save