milek7/sim branch opengl 3.3 renderer import

This commit is contained in:
tmj-fstate
2019-10-26 15:17:09 +02:00
parent d24fccb4d7
commit 0fd23a53d9
90 changed files with 7629 additions and 549 deletions

27
gl/bindable.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
namespace gl
{
template <typename T>
class bindable
{
protected:
thread_local static bindable<T>* active;
public:
void bind()
{
if (active == this)
return;
active = this;
T::bind(*static_cast<T*>(active));
}
static void unbind()
{
active = nullptr;
T::bind(0);
}
};
template <typename T> thread_local bindable<T>* bindable<T>::active;
}

89
gl/buffer.cpp Normal file
View File

@@ -0,0 +1,89 @@
#include "stdafx.h"
#include "buffer.h"
GLenum gl::buffer::glenum_target(gl::buffer::targets target)
{
static GLenum mapping[13] =
{
GL_ARRAY_BUFFER,
GL_ATOMIC_COUNTER_BUFFER,
GL_COPY_READ_BUFFER,
GL_COPY_WRITE_BUFFER,
GL_DISPATCH_INDIRECT_BUFFER,
GL_DRAW_INDIRECT_BUFFER,
GL_ELEMENT_ARRAY_BUFFER,
GL_PIXEL_PACK_BUFFER,
GL_PIXEL_UNPACK_BUFFER,
GL_SHADER_STORAGE_BUFFER,
GL_TEXTURE_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER,
GL_UNIFORM_BUFFER
};
return mapping[target];
}
void gl::buffer::bind(targets target)
{
if (binding_points[target] == *this)
return;
glBindBuffer(glenum_target(target), *this);
binding_points[target] = *this;
}
void gl::buffer::bind_base(targets target, GLuint index)
{
glBindBufferBase(glenum_target(target), index, *this);
binding_points[target] = *this;
}
void gl::buffer::unbind(targets target)
{
glBindBuffer(glenum_target(target), 0);
binding_points[target] = 0;
}
void gl::buffer::unbind()
{
for (size_t i = 0; i < sizeof(binding_points) / sizeof(GLuint); i++)
unbind((targets)i);
}
gl::buffer::buffer()
{
glGenBuffers(1, *this);
}
gl::buffer::~buffer()
{
glDeleteBuffers(1, *this);
}
void gl::buffer::allocate(targets target, int size, GLenum hint)
{
bind(target);
glBufferData(glenum_target(target), size, nullptr, hint);
}
void gl::buffer::upload(targets target, const void *data, int offset, int size)
{
bind(target);
glBufferSubData(glenum_target(target), offset, size, data);
}
void gl::buffer::download(targets target, void *data, int offset, int size)
{
bind(target);
if (GLAD_GL_VERSION_3_3)
{
glGetBufferSubData(glenum_target(target), offset, size, data);
}
else
{
void *glbuf = glMapBufferRange(glenum_target(target), offset, size, GL_MAP_READ_BIT);
memcpy(data, glbuf, size);
glUnmapBuffer(glenum_target(target));
}
}
thread_local GLuint gl::buffer::binding_points[13];

46
gl/buffer.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include "object.h"
#include "bindable.h"
namespace gl
{
class buffer : public object
{
thread_local static GLuint binding_points[13];
public:
enum targets
{
ARRAY_BUFFER = 0,
ATOMIC_COUNTER_BUFFER,
COPY_READ_BUFFER,
COPY_WRITE_BUFFER,
DISPATCH_INDIRECT_BUFFER,
DRAW_INDIRECT_BUFFER,
ELEMENT_ARRAY_BUFFER,
PIXEL_PACK_BUFFER,
PIXEL_UNPACK_BUFFER,
SHADER_STORAGE_BUFFER,
TEXTURE_BUFFER,
TRANSFORM_FEEDBACK_BUFFER,
UNIFORM_BUFFER
};
protected:
static GLenum glenum_target(targets target);
public:
buffer();
~buffer();
void bind(targets target);
void bind_base(targets target, GLuint index);
static void unbind(targets target);
static void unbind();
void allocate(targets target, int size, GLenum hint);
void upload(targets target, const void *data, int offset, int size);
void download(targets target, void *data, int offset, int size);
};
}

38
gl/cubemap.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include "stdafx.h"
#include "cubemap.h"
gl::cubemap::cubemap()
{
glGenTextures(1, *this);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
}
gl::cubemap::~cubemap()
{
glDeleteTextures(1, *this);
}
void gl::cubemap::alloc(GLint format, int width, int height, GLenum components, GLenum type)
{
glBindTexture(GL_TEXTURE_CUBE_MAP, *this);
for (GLuint tgt = GL_TEXTURE_CUBE_MAP_POSITIVE_X; tgt <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; tgt++)
glTexImage2D(tgt, 0, format, width, height, 0, components, type, nullptr);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
void gl::cubemap::bind(int unit)
{
glActiveTexture(unit);
glBindTexture(GL_TEXTURE_CUBE_MAP, *this);
}
void gl::cubemap::generate_mipmaps()
{
glBindTexture(GL_TEXTURE_CUBE_MAP, *this);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}

19
gl/cubemap.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "object.h"
namespace gl
{
// cubemap texture rendertarget
// todo: integrate with texture system
class cubemap : public object
{
public:
cubemap();
~cubemap();
void alloc(GLint format, int width, int height, GLenum components, GLenum type);
void bind(int unit);
void generate_mipmaps();
};
}

20
gl/fence.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "stdafx.h"
#include "fence.h"
gl::fence::fence()
{
sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
}
gl::fence::~fence()
{
glDeleteSync(sync);
}
bool gl::fence::is_signalled()
{
GLsizei len = 0;
GLint val;
glGetSynciv(sync, GL_SYNC_STATUS, 1, &len, &val);
return len == 1 && val == GL_SIGNALED;
}

20
gl/fence.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "object.h"
namespace gl
{
class fence
{
GLsync sync;
public:
fence();
~fence();
bool is_signalled();
fence(const fence&) = delete;
fence& operator=(const fence&) = delete;
};
}

103
gl/framebuffer.cpp Normal file
View File

@@ -0,0 +1,103 @@
#include "stdafx.h"
#include "framebuffer.h"
gl::framebuffer::framebuffer()
{
glGenFramebuffers(1, *this);
}
gl::framebuffer::~framebuffer()
{
glDeleteFramebuffers(1, *this);
}
void gl::framebuffer::bind(GLuint id)
{
glBindFramebuffer(GL_FRAMEBUFFER, id);
}
void gl::framebuffer::attach(const opengl_texture &tex, GLenum location)
{
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, location, tex.target, tex.id, 0);
}
void gl::framebuffer::attach(const cubemap &tex, int face, GLenum location)
{
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, location, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, *tex, 0);
}
void gl::framebuffer::attach(const renderbuffer &rb, GLenum location)
{
bind();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, location, GL_RENDERBUFFER, *rb);
}
void gl::framebuffer::detach(GLenum location)
{
bind();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, location, GL_RENDERBUFFER, 0);
}
bool gl::framebuffer::is_complete()
{
bind();
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
return status == GL_FRAMEBUFFER_COMPLETE;
}
void gl::framebuffer::clear(GLbitfield mask)
{
bind();
if (mask & GL_DEPTH_BUFFER_BIT)
glDepthMask(GL_TRUE);
glClear(mask);
}
void gl::framebuffer::blit_to(framebuffer *other, int w, int h, GLbitfield mask, GLenum attachment)
{
blit(this, other, 0, 0, w, h, mask, attachment);
}
void gl::framebuffer::blit_from(framebuffer *other, int w, int h, GLbitfield mask, GLenum attachment)
{
blit(other, this, 0, 0, w, h, mask, attachment);
}
void gl::framebuffer::blit(framebuffer *src, framebuffer *dst, int sx, int sy, int w, int h, GLbitfield mask, GLenum attachment)
{
glBindFramebuffer(GL_READ_FRAMEBUFFER, src ? *src : 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst ? *dst : 0);
if (mask & GL_COLOR_BUFFER_BIT)
{
int attachment_n = attachment - GL_COLOR_ATTACHMENT0;
GLenum outputs[8] = { GL_NONE };
outputs[attachment_n] = attachment;
glReadBuffer(attachment);
glDrawBuffers(attachment_n + 1, outputs);
}
glBlitFramebuffer(sx, sy, sx + w, sy + h, 0, 0, w, h, mask, GL_NEAREST);
unbind();
}
void gl::framebuffer::setup_drawing(int attachments)
{
bind();
GLenum a[8] =
{
GL_COLOR_ATTACHMENT0,
GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3,
GL_COLOR_ATTACHMENT4,
GL_COLOR_ATTACHMENT5,
GL_COLOR_ATTACHMENT6,
GL_COLOR_ATTACHMENT7
};
glDrawBuffers(std::min(attachments, 8), &a[0]);
}

33
gl/framebuffer.h Normal file
View File

@@ -0,0 +1,33 @@
#pragma once
#include "object.h"
#include "bindable.h"
#include "renderbuffer.h"
#include "Texture.h"
#include "cubemap.h"
namespace gl
{
class framebuffer : public object, public bindable<framebuffer>
{
public:
framebuffer();
~framebuffer();
void attach(const opengl_texture &tex, GLenum location);
void attach(const cubemap &tex, int face, GLenum location);
void attach(const renderbuffer &rb, GLenum location);
void setup_drawing(int attachments);
void detach(GLenum location);
void clear(GLbitfield mask);
bool is_complete();
void blit_to(framebuffer *other, int w, int h, GLbitfield mask, GLenum attachment);
void blit_from(framebuffer *other, int w, int h, GLbitfield mask, GLenum attachment);
static void blit(framebuffer *src, framebuffer *dst, int sx, int sy, int w, int h, GLbitfield mask, GLenum attachment);
using bindable::bind;
static void bind(GLuint id);
};
}

72
gl/glsl_common.cpp Normal file
View File

@@ -0,0 +1,72 @@
#include "stdafx.h"
#include "glsl_common.h"
std::string gl::glsl_common;
void gl::glsl_common_setup()
{
glsl_common =
"#define SHADOWMAP_ENABLED " + std::to_string((int)Global.gfx_shadowmap_enabled) + "\n" +
"#define ENVMAP_ENABLED " + std::to_string((int)Global.gfx_envmap_enabled) + "\n" +
"#define MOTIONBLUR_ENABLED " + std::to_string((int)Global.gfx_postfx_motionblur_enabled) + "\n" +
"#define POSTFX_ENABLED " + std::to_string((int)!Global.gfx_skippipeline) + "\n" +
"#define EXTRAEFFECTS_ENABLED " + std::to_string((int)Global.gfx_extraeffects) + "\n" +
"#define USE_GLES " + std::to_string((int)Global.gfx_usegles) + "\n" +
"const uint MAX_LIGHTS = " + std::to_string(MAX_LIGHTS) + "U;\n" +
"const uint MAX_PARAMS = " + std::to_string(MAX_PARAMS) + "U;\n" +
R"STRING(
const uint LIGHT_SPOT = 0U;
const uint LIGHT_POINT = 1U;
const uint LIGHT_DIR = 2U;
struct light_s
{
vec3 pos;
uint type;
vec3 dir;
float in_cutoff;
vec3 color;
float out_cutoff;
float linear;
float quadratic;
float intensity;
float ambient;
};
layout(std140) uniform light_ubo
{
vec3 ambient;
vec3 fog_color;
uint lights_count;
light_s lights[MAX_LIGHTS];
};
layout (std140) uniform model_ubo
{
mat4 modelview;
mat3 modelviewnormal;
vec4 param[MAX_PARAMS];
mat4 future;
float opacity;
float emission;
float fog_density;
float alpha_mult;
};
layout (std140) uniform scene_ubo
{
mat4 projection;
mat4 lightview;
vec3 scene_extra;
float time;
};
)STRING";
}

10
gl/glsl_common.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include "ubo.h"
#include "Globals.h"
namespace gl
{
extern std::string glsl_common;
void glsl_common_setup();
}

32
gl/object.h Normal file
View File

@@ -0,0 +1,32 @@
#pragma once
#include <glad/glad.h>
namespace gl
{
class object
{
private:
GLuint id = 0;
public:
inline operator GLuint() const
{
return id;
}
inline operator GLuint* const()
{
return &id;
}
inline operator const GLuint* const() const
{
return &id;
}
object() = default;
object(const object&) = delete;
object& operator=(const object&) = delete;
};
}

65
gl/pbo.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "stdafx.h"
#include "pbo.h"
void gl::pbo::request_read(int x, int y, int lx, int ly, int pixsize, GLenum format, GLenum type)
{
int s = lx * ly * pixsize;
if (s != size)
allocate(PIXEL_PACK_BUFFER, s, GL_STREAM_DRAW);
size = s;
data_ready = false;
sync.reset();
bind(PIXEL_PACK_BUFFER);
glReadPixels(x, y, lx, ly, format, type, 0);
unbind(PIXEL_PACK_BUFFER);
sync.emplace();
}
bool gl::pbo::read_data(int lx, int ly, void *data, int pixsize)
{
is_busy();
if (!data_ready)
return false;
int s = lx * ly * pixsize;
if (s != size)
return false;
download(PIXEL_PACK_BUFFER, data, 0, s);
unbind(PIXEL_PACK_BUFFER);
data_ready = false;
return true;
}
bool gl::pbo::is_busy()
{
if (!sync)
return false;
if (sync->is_signalled())
{
data_ready = true;
sync.reset();
return false;
}
return true;
}
void* gl::pbo::map(GLuint mode, targets target)
{
bind(target);
return glMapBuffer(buffer::glenum_target(target), mode);
}
void gl::pbo::unmap(targets target)
{
bind(target);
glUnmapBuffer(buffer::glenum_target(target));
sync.emplace();
}

22
gl/pbo.h Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include "buffer.h"
#include "fence.h"
#include <optional>
namespace gl {
class pbo : public buffer
{
std::optional<fence> sync;
int size = 0;
bool data_ready;
public:
void request_read(int x, int y, int lx, int ly, int pixsize = 4, GLenum format = GL_RGBA, GLenum type = GL_UNSIGNED_BYTE);
bool read_data(int lx, int ly, void *data, int pixsize = 4);
bool is_busy();
void* map(GLuint mode, targets target = PIXEL_UNPACK_BUFFER);
void unmap(targets target = PIXEL_UNPACK_BUFFER);
};
}

49
gl/postfx.cpp Normal file
View File

@@ -0,0 +1,49 @@
#include "stdafx.h"
#include "postfx.h"
std::shared_ptr<gl::shader> gl::postfx::vertex;
std::shared_ptr<gl::vao> gl::postfx::vao;
gl::postfx::postfx(const std::string &s) : postfx(shader("postfx_" + s + ".frag"))
{
}
gl::postfx::postfx(const shader &s)
{
if (!vertex)
vertex = std::make_shared<gl::shader>("quad.vert");
if (!vao)
vao = std::make_shared<gl::vao>();
program.attach(*vertex);
program.attach(s);
program.link();
}
void gl::postfx::apply(opengl_texture &src, framebuffer *dst)
{
apply({&src}, dst);
}
void gl::postfx::apply(std::vector<opengl_texture *> src, framebuffer *dst)
{
if (dst)
{
dst->clear(GL_COLOR_BUFFER_BIT);
dst->bind();
}
else
framebuffer::unbind();
program.bind();
vao->bind();
size_t unit = 0;
for (opengl_texture *tex : src)
tex->bind(unit++);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

25
gl/postfx.h Normal file
View File

@@ -0,0 +1,25 @@
#pragma once
#include "shader.h"
#include "vao.h"
#include "framebuffer.h"
#include "Texture.h"
namespace gl
{
class postfx
{
private:
gl::program program;
static std::shared_ptr<gl::shader> vertex;
static std::shared_ptr<gl::vao> vao;
public:
postfx(const std::string &s);
postfx(const shader &s);
void attach();
void apply(opengl_texture &src, framebuffer *dst);
void apply(std::vector<opengl_texture*> src, framebuffer *dst);
};
}

65
gl/query.cpp Normal file
View File

@@ -0,0 +1,65 @@
#include "stdafx.h"
#include "query.h"
#include "Globals.h"
gl::query::query(targets target)
: target(target)
{
glGenQueries(1, *this);
}
gl::query::~query()
{
end();
glDeleteQueries(1, *this);
}
void gl::query::begin()
{
if (active_queries[target])
active_queries[target]->end();
glBeginQuery(glenum_target(target), *this);
active_queries[target] = this;
}
void gl::query::end()
{
if (active_queries[target] != this)
return;
glEndQuery(glenum_target(target));
active_queries[target] = nullptr;
}
std::optional<int64_t> gl::query::result()
{
GLuint ready;
glGetQueryObjectuiv(*this, GL_QUERY_RESULT_AVAILABLE, &ready);
int64_t value = 0;
if (ready) {
if (!Global.gfx_usegles)
glGetQueryObjecti64v(*this, GL_QUERY_RESULT, &value);
else
glGetQueryObjectuiv(*this, GL_QUERY_RESULT, reinterpret_cast<GLuint*>(&value));
return std::optional<int64_t>(value);
}
return std::nullopt;
}
GLenum gl::query::glenum_target(targets target)
{
static GLenum mapping[6] =
{
GL_SAMPLES_PASSED,
GL_ANY_SAMPLES_PASSED,
GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
GL_PRIMITIVES_GENERATED,
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
GL_TIME_ELAPSED
};
return mapping[target];
}
thread_local gl::query* gl::query::active_queries[6];

36
gl/query.h Normal file
View File

@@ -0,0 +1,36 @@
#pragma once
#include "object.h"
namespace gl
{
class query : public object
{
public:
enum targets
{
SAMPLES_PASSED = 0,
ANY_SAMPLES_PASSED,
ANY_SAMPLES_PASSED_CONSERVATIVE,
PRIMITIVES_GENERATED,
TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
TIME_ELAPSED
};
private:
targets target;
thread_local static query* active_queries[6];
protected:
GLenum glenum_target(targets target);
public:
query(targets target);
~query();
void begin();
void end();
std::optional<int64_t> result();
};
}

27
gl/renderbuffer.cpp Normal file
View File

@@ -0,0 +1,27 @@
#include "stdafx.h"
#include "renderbuffer.h"
gl::renderbuffer::renderbuffer()
{
glGenRenderbuffers(1, *this);
}
gl::renderbuffer::~renderbuffer()
{
glDeleteRenderbuffers(1, *this);
}
void gl::renderbuffer::bind(GLuint id)
{
glBindRenderbuffer(GL_RENDERBUFFER, id);
}
void gl::renderbuffer::alloc(GLuint format, int width, int height, int samples)
{
bind();
if (samples == 1)
glRenderbufferStorage(GL_RENDERBUFFER, format, width, height);
else
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, format, width, height);
}

19
gl/renderbuffer.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "object.h"
#include "bindable.h"
namespace gl
{
class renderbuffer : public object, public bindable<renderbuffer>
{
public:
renderbuffer();
~renderbuffer();
void alloc(GLuint format, int width, int height, int samples = 1);
static void bind(GLuint id);
using bindable::bind;
};
}

334
gl/shader.cpp Normal file
View File

@@ -0,0 +1,334 @@
#include "stdafx.h"
#include <fstream>
#include <sstream>
#include "shader.h"
#include "glsl_common.h"
#include "Logs.h"
inline bool strcend(std::string const &value, std::string const &ending)
{
if (ending.size() > value.size())
return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
std::string gl::shader::read_file(const std::string &filename)
{
std::stringstream stream;
std::ifstream f;
f.exceptions(std::ifstream::badbit);
f.open("shaders/" + filename);
stream << f.rdbuf();
f.close();
std::string str = stream.str();
return str;
}
void gl::shader::expand_includes(std::string &str)
{
size_t start_pos = 0;
std::string magic = "#include";
while ((start_pos = str.find(magic, start_pos)) != str.npos)
{
size_t fp = str.find('<', start_pos);
size_t fe = str.find('>', start_pos);
if (fp == str.npos || fe == str.npos)
return;
std::string filename = str.substr(fp + 1, fe - fp - 1);
std::string content;
if (filename != "common")
content = read_file(filename);
else
content = glsl_common;
str.replace(start_pos, fe - start_pos + 1, content);
}
}
std::unordered_map<std::string, gl::shader::components_e> gl::shader::components_mapping =
{
{ "R", components_e::R },
{ "RG", components_e::RG },
{ "RGB", components_e::RGB },
{ "RGBA", components_e::RGBA },
{ "sRGB", components_e::sRGB },
{ "sRGB_A", components_e::sRGB_A }
};
std::unordered_map<std::string, gl::shader::defaultparam_e> gl::shader::defaultparams_mapping =
{
{ "required", defaultparam_e::required },
{ "nan", defaultparam_e::nan },
{ "zero", defaultparam_e::zero },
{ "one", defaultparam_e::one },
{ "ambient", defaultparam_e::ambient },
{ "diffuse", defaultparam_e::diffuse },
{ "specular", defaultparam_e::specular }
};
void gl::shader::process_source(std::string &str)
{
expand_includes(str);
parse_texture_entries(str);
parse_param_entries(str);
}
void gl::shader::parse_texture_entries(std::string &str)
{
size_t start_pos = 0;
std::string magic = "#texture";
while ((start_pos = str.find(magic, start_pos)) != str.npos)
{
size_t fp = str.find('(', start_pos);
size_t fe = str.find(')', start_pos);
if (fp == str.npos || fe == str.npos)
return;
std::istringstream ss(str.substr(fp + 1, fe - fp - 1));
std::string token;
std::string name;
texture_entry conf;
size_t arg = 0;
while (std::getline(ss, token, ','))
{
std::istringstream token_ss(token);
if (arg == 0)
token_ss >> name;
else if (arg == 1)
token_ss >> conf.id;
else if (arg == 2)
{
std::string comp;
token_ss >> comp;
if (components_mapping.find(comp) == components_mapping.end())
log_error("unknown components: " + comp);
else
conf.components = components_mapping[comp];
}
arg++;
}
if (arg == 3)
{
if (name.empty())
log_error("empty name");
else if (conf.id >= gl::MAX_TEXTURES)
log_error("invalid texture binding: " + std::to_string(conf.id));
else
texture_conf.emplace(std::make_pair(name, conf));
}
else
log_error("invalid argument count to #texture");
str.erase(start_pos, fe - start_pos + 1);
}
}
void gl::shader::parse_param_entries(std::string &str)
{
size_t start_pos = 0;
std::string magic = "#param";
while ((start_pos = str.find(magic, start_pos)) != str.npos)
{
size_t fp = str.find('(', start_pos);
size_t fe = str.find(')', start_pos);
if (fp == str.npos || fe == str.npos)
return;
std::istringstream ss(str.substr(fp + 1, fe - fp - 1));
std::string token;
std::string name;
param_entry conf;
size_t arg = 0;
while (std::getline(ss, token, ','))
{
std::istringstream token_ss(token);
if (arg == 0)
token_ss >> name;
else if (arg == 1)
token_ss >> conf.location;
else if (arg == 2)
token_ss >> conf.offset;
else if (arg == 3)
token_ss >> conf.size;
else if (arg == 4)
{
std::string tok;
token_ss >> tok;
if (defaultparams_mapping.find(tok) == defaultparams_mapping.end())
log_error("unknown param default: " + tok);
conf.defaultparam = defaultparams_mapping[tok];
}
arg++;
}
if (arg == 5)
{
if (name.empty())
log_error("empty name");
else if (conf.location >= gl::MAX_PARAMS)
log_error("invalid param binding: " + std::to_string(conf.location));
else if (conf.offset > 3)
log_error("invalid offset: " + std::to_string(conf.offset));
else if (conf.offset + conf.size > 4)
log_error("invalid size: " + std::to_string(conf.size));
else
param_conf.emplace(std::make_pair(name, conf));
}
else
log_error("invalid argument count to #param");
str.erase(start_pos, fe - start_pos + 1);
}
}
void gl::shader::log_error(const std::string &str)
{
ErrorLog("bad shader: " + name + ": " + str, logtype::shader);
}
gl::shader::shader(const std::string &filename)
{
name = filename;
GLuint type;
if (strcend(filename, ".vert"))
type = GL_VERTEX_SHADER;
else if (strcend(filename, ".frag"))
type = GL_FRAGMENT_SHADER;
else if (strcend(filename, ".geom"))
type = GL_GEOMETRY_SHADER;
else
throw shader_exception("unknown shader " + filename);
std::string str;
if (!Global.gfx_usegles)
{
str += "#version 330 core\n";
}
else
{
if (GLAD_GL_ES_VERSION_3_1) {
str += "#version 310 es\n";
if (type == GL_GEOMETRY_SHADER)
str += "#extension GL_EXT_geometry_shader : require\n";
} else {
str += "#version 300 es\n";
}
str += "precision highp float;\n";
str += "precision highp sampler2DShadow;\n";
}
str += "vec4 FBOUT(vec4 x) { return " + (Global.gfx_shadergamma ? std::string("vec4(pow(x.rgb, vec3(1.0 / 2.2)), x.a)") : std::string("x")) + "; }\n";
str += read_file(filename);
process_source(str);
const GLchar *cstr = str.c_str();
if (!cstr[0])
throw shader_exception("cannot read shader: " + filename);
**this = glCreateShader(type);
glShaderSource(*this, 1, &cstr, 0);
glCompileShader(*this);
GLint status;
glGetShaderiv(*this, GL_COMPILE_STATUS, &status);
if (!status)
{
GLchar info[512];
glGetShaderInfoLog(*this, 512, 0, info);
std::cerr << std::string(info) << std::endl;
throw shader_exception("failed to compile " + filename + ": " + std::string(info));
}
}
gl::shader::~shader()
{
glDeleteShader(*this);
}
void gl::program::init()
{
bind();
for (auto it : texture_conf)
{
shader::texture_entry &e = it.second;
GLuint loc = glGetUniformLocation(*this, it.first.c_str());
glUniform1i(loc, e.id);
}
glUniform1i(glGetUniformLocation(*this, "shadowmap"), MAX_TEXTURES + 0);
glUniform1i(glGetUniformLocation(*this, "envmap"), MAX_TEXTURES + 1);
GLuint index;
if ((index = glGetUniformBlockIndex(*this, "scene_ubo")) != GL_INVALID_INDEX)
glUniformBlockBinding(*this, 0, index);
if ((index = glGetUniformBlockIndex(*this, "model_ubo")) != GL_INVALID_INDEX)
glUniformBlockBinding(*this, 1, index);
if ((index = glGetUniformBlockIndex(*this, "light_ubo")) != GL_INVALID_INDEX)
glUniformBlockBinding(*this, 2, index);
}
gl::program::program()
{
**this = glCreateProgram();
}
gl::program::program(std::vector<std::reference_wrapper<const gl::shader>> shaders) : program()
{
for (const gl::shader &s : shaders)
attach(s);
link();
}
void gl::program::attach(const gl::shader &s)
{
for (auto it : s.texture_conf)
texture_conf.emplace(std::make_pair(it.first, std::move(it.second)));
for (auto it : s.param_conf)
param_conf.emplace(std::make_pair(it.first, std::move(it.second)));
glAttachShader(*this, *s);
}
void gl::program::link()
{
glLinkProgram(*this);
GLint status;
glGetProgramiv(*this, GL_LINK_STATUS, &status);
if (!status)
{
GLchar info[512];
glGetProgramInfoLog(*this, 512, 0, info);
throw shader_exception("failed to link program: " + std::string(info));
}
init();
}
gl::program::~program()
{
glDeleteProgram(*this);
}
void gl::program::bind(GLuint i)
{
glUseProgram(i);
}

96
gl/shader.h Normal file
View File

@@ -0,0 +1,96 @@
#pragma once
#include <string>
#include <vector>
#include <functional>
#include "object.h"
#include "bindable.h"
namespace gl
{
class shader_exception : public std::runtime_error
{
using runtime_error::runtime_error;
};
class shader : public object
{
public:
shader(const std::string &filename);
~shader();
enum class components_e
{
R = GL_RED,
RG = GL_RG,
RGB = GL_RGB,
RGBA = GL_RGBA,
sRGB = GL_SRGB,
sRGB_A = GL_SRGB_ALPHA
};
struct texture_entry
{
size_t id;
components_e components;
};
enum class defaultparam_e
{
required,
nan,
zero,
one,
ambient,
diffuse,
specular
};
struct param_entry
{
size_t location;
size_t offset;
size_t size;
defaultparam_e defaultparam;
};
std::unordered_map<std::string, texture_entry> texture_conf;
std::unordered_map<std::string, param_entry> param_conf;
std::string name;
private:
void process_source(std::string &str);
void expand_includes(std::string &str);
void parse_texture_entries(std::string &str);
void parse_param_entries(std::string &str);
std::string read_file(const std::string &filename);
static std::unordered_map<std::string, components_e> components_mapping;
static std::unordered_map<std::string, defaultparam_e> defaultparams_mapping;
void log_error(const std::string &str);
};
class program : public object, public bindable<program>
{
public:
program();
program(std::vector<std::reference_wrapper<const gl::shader>>);
~program();
using bindable::bind;
static void bind(GLuint i);
void attach(const shader &);
void link();
std::unordered_map<std::string, shader::texture_entry> texture_conf;
std::unordered_map<std::string, shader::param_entry> param_conf;
private:
void init();
};
}

19
gl/ubo.cpp Normal file
View File

@@ -0,0 +1,19 @@
#include "stdafx.h"
#include "ubo.h"
gl::ubo::ubo(int size, int idx, GLenum hint)
{
allocate(buffer::UNIFORM_BUFFER, size, hint);
index = idx;
bind_uniform();
}
void gl::ubo::bind_uniform()
{
bind_base(buffer::UNIFORM_BUFFER, index);
}
void gl::ubo::update(const uint8_t *data, int offset, int size)
{
upload(buffer::UNIFORM_BUFFER, data, offset, size);
}

113
gl/ubo.h Normal file
View File

@@ -0,0 +1,113 @@
#pragma once
#include "object.h"
#include "bindable.h"
#include "buffer.h"
#define UBS_PAD(x) uint8_t PAD[x]
namespace gl
{
class ubo : public buffer
{
int index;
public:
ubo(int size, int index, GLenum hint = GL_DYNAMIC_DRAW);
void bind_uniform();
void update(const uint8_t *data, int offset, int size);
template <typename T> void update(const T &data, size_t offset = 0)
{
update(reinterpret_cast<const uint8_t*>(&data), offset, sizeof(data));
}
};
// layout std140
// structs must match with GLSL
// ordered to minimize padding
#pragma pack(push, 1)
const size_t MAX_TEXTURES = 8;
const size_t ENVMAP_SIZE = 1024;
struct scene_ubs
{
glm::mat4 projection;
glm::mat4 lightview;
glm::vec3 scene_extra;
float time;
};
static_assert(sizeof(scene_ubs) == 144, "bad size of ubs");
const size_t MAX_PARAMS = 3;
struct model_ubs
{
glm::mat4 modelview;
glm::mat3x4 modelviewnormal;
glm::vec4 param[MAX_PARAMS];
glm::mat4 future;
float opacity;
float emission;
float fog_density;
float alpha_mult;
UBS_PAD(4);
void set_modelview(const glm::mat4 &mv)
{
modelview = mv;
modelviewnormal = glm::mat3x4(glm::mat3(glm::transpose(glm::inverse(mv))));
}
};
static_assert(sizeof(model_ubs) == 196 + 16 * MAX_PARAMS, "bad size of ubs");
struct light_element_ubs
{
enum type_e
{
SPOT = 0,
POINT,
DIR
};
glm::vec3 pos;
type_e type;
glm::vec3 dir;
float in_cutoff;
glm::vec3 color;
float out_cutoff;
float linear;
float quadratic;
float intensity;
float ambient;
};
static_assert(sizeof(light_element_ubs) == 64, "bad size of ubs");
const size_t MAX_LIGHTS = 8;
struct light_ubs
{
glm::vec3 ambient;
UBS_PAD(4);
glm::vec3 fog_color;
uint32_t lights_count;
light_element_ubs lights[MAX_LIGHTS];
};
static_assert(sizeof(light_ubs) == 32 + sizeof(light_element_ubs) * MAX_LIGHTS, "bad size of ubs");
#pragma pack(pop)
}

85
gl/vao.cpp Normal file
View File

@@ -0,0 +1,85 @@
#include "stdafx.h"
#include "vao.h"
bool gl::vao::use_vao = true;
gl::vao::vao()
{
if (!use_vao)
return;
glGenVertexArrays(1, *this);
}
gl::vao::~vao()
{
if (!use_vao)
return;
unbind();
glDeleteVertexArrays(1, *this);
}
void gl::vao::setup_attrib(gl::buffer &buffer, int attrib, int size, int type, int stride, int offset)
{
if (use_vao) {
bind();
buffer.bind(buffer::ARRAY_BUFFER);
glVertexAttribPointer(attrib, size, type, GL_FALSE, stride, reinterpret_cast<void*>(offset));
glEnableVertexAttribArray(attrib);
}
else {
params.push_back({buffer, attrib, size, type, stride, offset});
active = nullptr;
}
}
void gl::vao::setup_ebo(buffer &e)
{
if (use_vao) {
bind();
e.bind(buffer::ELEMENT_ARRAY_BUFFER);
}
else {
ebo = &e;
active = nullptr;
}
}
void gl::vao::bind()
{
if (active == this)
return;
active = this;
if (use_vao) {
glBindVertexArray(*this);
}
else {
for (attrib_params &param : params) {
param.buffer.bind(gl::buffer::ARRAY_BUFFER);
glVertexAttribPointer(param.attrib, param.size, param.type, GL_FALSE, param.stride, reinterpret_cast<void*>(param.offset));
glEnableVertexAttribArray(param.attrib);
}
for (size_t i = params.size(); i < 4; i++)
glDisableVertexAttribArray(i);
if (ebo)
ebo->bind(gl::buffer::ELEMENT_ARRAY_BUFFER);
else
gl::buffer::unbind(gl::buffer::ELEMENT_ARRAY_BUFFER);
}
}
void gl::vao::unbind()
{
active = nullptr;
if (use_vao) {
glBindVertexArray(0);
}
else {
for (size_t i = 0; i < 4; i++)
glDisableVertexAttribArray(i);
}
}

37
gl/vao.h Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include "object.h"
#include "bindable.h"
#include "buffer.h"
namespace gl
{
class vao : object, public bindable<vao>
{
struct attrib_params {
// TBD: should be shared_ptr? (when buffer is destroyed by owner VAO could still potentially exist)
gl::buffer &buffer;
int attrib;
int size;
int type;
int stride;
int offset;
};
buffer *ebo = nullptr;
std::vector<attrib_params> params;
public:
vao();
~vao();
void setup_attrib(buffer &buffer, int attrib, int size, int type, int stride, int offset);
void setup_ebo(buffer &ebo);
void bind();
static void unbind();
static bool use_vao;
};
}