Add templated wrapper objects

- Shader
- Program
- Buffer
- VertexArray

Along with template magic to deduce VertexArray formats

- Format, IFormat, LFormat
- AutoFormat
- Binder
- ATTR
This commit is contained in:
2022-02-13 20:28:00 -05:00
committed by David Allemang
parent 669d04845d
commit 89b9780f6c
7 changed files with 360 additions and 144 deletions

View File

@@ -19,7 +19,13 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/res ${CMAKE_CURRENT_BINARY_DIR}/res COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/res ${CMAKE_CURRENT_BINARY_DIR}/res
COMMENT "Copying Resources") COMMENT "Copying Resources")
add_executable(vis src/main.cpp) add_executable(vis
src/main.cpp
src/gl/debug.hpp
src/gl/shader.hpp
src/gl/buffer.hpp
src/gl/vertexarray.hpp
src/gl/types.hpp)
target_link_libraries(vis glfw glad imgui eigen nlohmann_json) target_link_libraries(vis glfw glad imgui eigen nlohmann_json)
add_dependencies(vis resources) add_dependencies(vis resources)

48
src/gl/buffer.hpp Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <glad/glad.h>
#include <utility>
template<typename T_>
class Buffer {
public:
using Type = T_;
private:
GLuint id = 0;
public:
Buffer() {
glCreateBuffers(1, &id);
}
Buffer(Buffer&& o) noexcept {
id = std::exchange(o.id, 0);
}
Buffer(const Buffer&) = delete; // this is doable, but would be slow.
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
template<typename RandomIt>
GLuint upload(RandomIt first, RandomIt last, GLenum mode = GL_STATIC_DRAW) {
size_t count = last - first;
// todo StaticBuffer that uses BufferStorage
glNamedBufferData(id, sizeof(Type) * count, nullptr, mode);
Type* out = (Type*) glMapNamedBuffer(id, GL_WRITE_ONLY);
std::copy(first, last, out);
glUnmapNamedBuffer(id);
return count;
}
~Buffer() {
// delete silently ignores 0.
glDeleteBuffers(1, &id);
}
};

79
src/gl/shader.hpp Normal file
View File

@@ -0,0 +1,79 @@
#pragma once
#include <glad/glad.h>
#include <string>
#include <memory>
#include <vector>
template<GLenum mode>
class Shader {
public:
private:
GLuint id = 0;
public:
explicit Shader(const std::string &src) {
id = glCreateShader(mode);
const char *str = src.c_str();
glShaderSource(id, 1, &str, nullptr);
glCompileShader(id);
// todo throw if compile failed
}
explicit Shader(std::ifstream source)
: Shader(std::string(
std::istreambuf_iterator<char>(source),
std::istreambuf_iterator<char>()
)) {}
Shader(const Shader &) = delete;
Shader(Shader &&o) noexcept {
id = std::exchange(o.id, 0);
}
~Shader() {
glDeleteShader(id);
}
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
};
using VertexShader = Shader<GL_VERTEX_SHADER>;
using FragmentShader = Shader<GL_FRAGMENT_SHADER>;
class Program {
private:
GLuint id = 0;
public:
template<GLenum ...mode>
explicit Program(const Shader<mode> &...shader) {
id = glCreateProgram();
(glAttachShader(id, shader), ...);
glLinkProgram(id);
// todo throw if link failed
}
Program(const Program &) = delete;
Program(Program &&o) noexcept {
id = std::exchange(o.id, 0);
}
~Program() {
glDeleteProgram(id);
}
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
};

113
src/gl/types.hpp Normal file
View File

@@ -0,0 +1,113 @@
#pragma once
#include <glad/glad.h>
#include <Eigen/Eigen>
template<class T_>
struct Buffer;
template<class ...Fmt_>
struct VertexArray;
template<typename ctype, GLenum type, GLint size, GLuint offset = 0, GLboolean norm = GL_FALSE>
struct Format {
using CType = ctype;
static constexpr GLenum Type = type;
static constexpr GLint Size = size;
static constexpr GLuint Offset = offset;
static constexpr GLboolean Norm = norm;
template<typename ctype_, GLint size_>
using As = Format<ctype_, type, size_, offset, norm>;
static inline constexpr void apply(GLuint vao, GLuint idx) {
glEnableVertexArrayAttrib(vao, idx);
glVertexArrayAttribFormat(vao, idx, size, type, norm, offset);
}
};
template<typename ctype, GLenum type, GLint size, GLuint offset = 0>
struct IFormat {
using CType = ctype;
static constexpr GLenum Type = type;
static constexpr GLint Size = size;
static constexpr GLuint Offset = offset;
template<typename ctype_, GLint size_>
using As = IFormat<ctype_, type, size_, offset>;
static inline constexpr void apply(GLuint vao, GLuint idx) {
glEnableVertexArrayAttrib(vao, idx);
glVertexArrayAttribIFormat(vao, idx, size, type, offset);
}
};
template<typename ctype, GLenum type, GLint size, GLuint offset = 0>
struct LFormat {
using CType = ctype;
static constexpr GLenum Type = type;
static constexpr GLint Size = size;
static constexpr GLuint Offset = offset;
template<typename ctype_, GLint size_>
using As = LFormat<ctype_, type, size_, offset>;
static inline constexpr void apply(GLuint vao, GLuint idx) {
glEnableVertexArrayAttrib(vao, idx);
glVertexArrayAttribLFormat(vao, idx, size, type, offset);
}
};
template<typename Fmt_>
struct AutoFormat {
using Fmt = Fmt_;
};
template<>
struct AutoFormat<double> {
using Fmt = LFormat<double, GL_DOUBLE, 1>;
};
template<>
struct AutoFormat<float> {
using Fmt = Format<float, GL_FLOAT, 1>;
};
template<>
struct AutoFormat<int> {
using Fmt = IFormat<int, GL_INT, 1>;
};
template<typename Scalar, int Dim>
struct AutoFormat<Eigen::Vector<Scalar, Dim>> {
static_assert((Dim >= 1) && (Dim <= 4), "Dim must be in range 1-4");
using Fmt = typename AutoFormat<Scalar>::Fmt::template As<Eigen::Vector<Scalar, Dim>, Dim>;
};
template<typename Fmt_>
struct Binder {
using CType = typename AutoFormat<Fmt_>::Fmt::CType;
const GLuint buf;
const GLuint offset;
const GLuint stride;
Binder(const Buffer<CType> &buf) // NOLINT(google-explicit-constructor)
: buf((GLuint) buf), offset(0), stride(sizeof(CType)) {}
template<typename CType_>
Binder(const Buffer<CType_> &buf, GLuint offset)
: buf((GLuint) buf), offset(offset), stride(sizeof(CType_)) {}
Binder(const Binder<Fmt_> &o)
: buf(o.buf), offset(o.offset), stride(o.stride) {}
inline void bind(GLuint vao, GLuint idx) const {
glVertexArrayVertexBuffer(vao, idx, buf, offset, stride);
}
};
#define ATTR(buf, field) Binder<decltype(decltype(buf)::Type::field)>(buf, offsetof(decltype(buf)::Type, field))

67
src/gl/vertexarray.hpp Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
#include <glad/glad.h>
#include <utility>
#include "buffer.hpp"
#include "types.hpp"
template<typename ...Fmt_>
class VertexArray {
public:
template<size_t idx>
using Fmt = std::tuple_element_t<idx, std::tuple<Fmt_...>>;
private:
GLuint id = 0;
template<size_t ...idx>
inline void formatall(
std::integer_sequence<size_t, idx...>
) {
(AutoFormat<Fmt_>::Fmt::apply(id, idx), ...);
}
template<size_t ...idx>
inline void bindall(
const Binder<Fmt_> &...buf,
std::integer_sequence<size_t, idx...>
) {
(buf.bind(id, idx), ...);
}
public:
explicit VertexArray() {
glCreateVertexArrays(1, &id);
formatall(std::make_index_sequence<sizeof...(Fmt_)>());
}
explicit VertexArray(const Binder<Fmt_> &...buf)
: VertexArray() {
bind(buf...);
}
VertexArray(VertexArray &&o) noexcept {
id = std::exchange(o.id, 0);
}
VertexArray(const VertexArray &) = delete; // this is doable, but would be slow.
~VertexArray() {
glDeleteVertexArrays(1, &id);
}
operator GLuint() const { // NOLINT(google-explicit-constructor)
return id;
}
void bind(const Binder<Fmt_> &...buf) {
bindall(buf..., std::make_index_sequence<sizeof...(Fmt_)>());
}
template<size_t idx>
void bind(const Binder<Fmt<idx>> &buf) {
buf.bind(id, idx);
}
};

View File

@@ -6,7 +6,10 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include "gldebug.hpp" #include "gl/debug.hpp"
#include "gl/buffer.hpp"
#include "gl/shader.hpp"
#include "gl/vertexarray.hpp"
#include <ml/meshlib.hpp> #include <ml/meshlib.hpp>
#include <ml/meshlib_json.hpp> #include <ml/meshlib_json.hpp>
@@ -123,145 +126,54 @@ void set_style() {
int run(GLFWwindow *window, ImGuiContext *context) { int run(GLFWwindow *window, ImGuiContext *context) {
State state; State state;
Buffer<GLuint> ind_buf;
Buffer<Eigen::Vector3f> vert_buf;
Buffer<Eigen::Vector3f> vert2_buf;
VertexArray<Eigen::Vector3f> vao(vert_buf);
glVertexArrayElementBuffer(vao, ind_buf);
auto mesh = ml::CubeMesh(0.25f); auto mesh = ml::CubeMesh(0.25f);
// auto mesh = ml::read("circle.pak");
auto dynamic = (ml::DynamicMesh) mesh; auto ind_data = mesh.cells();
auto ind_flat = ind_data.reshaped();
auto elements = ind_buf.upload(ind_flat.begin(), ind_flat.end(), GL_STATIC_DRAW);
GLuint vao; // todo add <Dim, Rank> to DynamicMesh
glCreateVertexArrays(1, &vao); // need to do weird piping because dynamicmesh returns dynamic-sized matrix, VertexArray requires a static-sized matrix
auto vert_data_dyn = mesh.points();
Eigen::Ref<Eigen::Matrix3Xf> vert_data(vert_data_dyn);
auto vert_flat = vert_data.colwise();
vert_buf.upload(vert_flat.begin(), vert_flat.end(), GL_STATIC_DRAW);
GLuint vbo; auto mesh2 = ml::CubeMesh(0.5f);
glCreateBuffers(1, &vbo); auto vert2_data_dyn = mesh2.points();
Eigen::Ref<Eigen::Matrix3Xf> vert2_data(vert2_data_dyn);
auto vert2_flat = vert2_data.colwise();
vert2_buf.upload(vert2_flat.begin(), vert2_flat.end(), GL_STATIC_DRAW);
constexpr size_t point_scalar_size = sizeof(ml::DynamicMesh::PointsType::Scalar); auto mesh4d = ml::WireCubeMesh(4, 0.33f);
constexpr size_t cell_scalar_size = sizeof(ml::DynamicMesh::CellsType::Scalar);
glNamedBufferData( Buffer<GLuint> ind4d_buf;
vbo, Buffer<Eigen::Vector4f> vert4d_buf;
(GLsizeiptr) (point_scalar_size * dynamic.points().size()), VertexArray<Eigen::Vector4f> vao4d(vert4d_buf);
dynamic.points().data(), glVertexArrayElementBuffer(vao4d, ind4d_buf);
GL_STATIC_DRAW
);
glEnableVertexArrayAttrib(vao, 0);
glVertexArrayVertexBuffer(vao, 0,
vbo, 0,
(GLsizeiptr) (point_scalar_size * dynamic.points().rows())
);
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
GLuint ibo; auto ind4d_data = mesh4d.cells();
glCreateBuffers(1, &ibo); auto ind4d_flat = ind4d_data.reshaped();
glGetError(); auto elements4d = ind4d_buf.upload(ind4d_flat.begin(), ind4d_flat.end(), GL_STATIC_DRAW);
glNamedBufferData(
ibo,
(GLsizeiptr) (cell_scalar_size * dynamic.cells().size()),
dynamic.cells().data(),
GL_STATIC_DRAW
);
glVertexArrayElementBuffer(vao, ibo);
auto wire_mesh = ml::WireCubeMesh(4, 0.33f); auto vert4d_data_dyn = mesh4d.points();
auto wire_dynamic = (ml::DynamicMesh) wire_mesh; Eigen::Ref<Eigen::Matrix4Xf> vert4d_data(vert4d_data_dyn);
auto vert4d_flat = vert4d_data.colwise();
vert4d_buf.upload(vert4d_flat.begin(), vert4d_flat.end(), GL_STATIC_DRAW);
GLuint wire_vao; VertexShader vs(std::ifstream("res/shaders/main.vert.glsl"));
glCreateVertexArrays(1, &wire_vao); VertexShader vs4d(std::ifstream("res/shaders/4d.vert.glsl"));
FragmentShader fs(std::ifstream("res/shaders/main.frag.glsl"));
GLuint wire_vbo; Program pgm(vs, fs);
glCreateBuffers(1, &wire_vbo); Program pgm4d(vs4d, fs);
glNamedBufferData(
wire_vbo,
(GLsizeiptr) (point_scalar_size * wire_dynamic.points().size()),
wire_dynamic.points().data(),
GL_STATIC_DRAW
);
glEnableVertexArrayAttrib(wire_vao, 0);
glVertexArrayVertexBuffer(wire_vao, 0,
wire_vbo, 0,
(GLsizeiptr) (point_scalar_size * wire_dynamic.points().rows())
);
glVertexArrayAttribFormat(wire_vao, 0, 4, GL_FLOAT, GL_FALSE, 0);
GLuint wire_ibo;
glCreateBuffers(1, &wire_ibo);
glGetError();
glNamedBufferData(
wire_ibo,
(GLsizeiptr) (cell_scalar_size * wire_dynamic.cells().size()),
wire_dynamic.cells().data(),
GL_STATIC_DRAW
);
glVertexArrayElementBuffer(wire_vao, wire_ibo);
std::ifstream vs_file("res/shaders/main.vert.glsl");
std::string vs_src(
(std::istreambuf_iterator<char>(vs_file)),
std::istreambuf_iterator<char>()
);
vs_file.close();
const char *vs_str = vs_src.c_str();
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vs_str, nullptr);
glCompileShader(vs);
std::ifstream wire_vs_file("res/shaders/4d.vert.glsl");
std::string wire_vs_src(
(std::istreambuf_iterator<char>(wire_vs_file)),
std::istreambuf_iterator<char>()
);
wire_vs_file.close();
const char *wire_vs_str = wire_vs_src.c_str();
GLuint wire_vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(wire_vs, 1, &wire_vs_str, nullptr);
glCompileShader(wire_vs);
std::ifstream fs_file("res/shaders/main.frag.glsl");
std::string fs_src(
(std::istreambuf_iterator<char>(fs_file)),
std::istreambuf_iterator<char>()
);
fs_file.close();
const char *fs_str = fs_src.c_str();
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fs_str, nullptr);
glCompileShader(fs);
GLuint pgm = glCreateProgram();
glAttachShader(pgm, vs);
glAttachShader(pgm, fs);
glLinkProgram(pgm);
GLuint wire_pgm = glCreateProgram();
glAttachShader(wire_pgm, wire_vs);
glAttachShader(wire_pgm, fs);
glLinkProgram(wire_pgm);
GLint link_status;
glGetProgramiv(pgm, GL_LINK_STATUS, &link_status);
if (!link_status) {
std::cerr << "Program link failed." << std::endl;
GLint vs_comp_status, fs_comp_status;
glGetShaderiv(vs, GL_COMPILE_STATUS, &vs_comp_status);
glGetShaderiv(fs, GL_COMPILE_STATUS, &fs_comp_status);
std::cerr << "vs compiled: " << std::boolalpha << (bool) vs_comp_status << std::endl;
std::cerr << "fs compiled: " << std::boolalpha << (bool) fs_comp_status << std::endl;
return EXIT_FAILURE;
}
glGetProgramiv(wire_pgm, GL_LINK_STATUS, &link_status);
if (!link_status) {
std::cerr << "Wire program link failed." << std::endl;
GLint vs_comp_status, fs_comp_status;
glGetShaderiv(vs, GL_COMPILE_STATUS, &vs_comp_status);
glGetShaderiv(fs, GL_COMPILE_STATUS, &fs_comp_status);
std::cerr << "vs compiled: " << std::boolalpha << (bool) vs_comp_status << std::endl;
std::cerr << "fs compiled: " << std::boolalpha << (bool) fs_comp_status << std::endl;
return EXIT_FAILURE;
}
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
@@ -291,12 +203,12 @@ int run(GLFWwindow *window, ImGuiContext *context) {
glUniform1f(1, (GLfloat) glfwGetTime()); glUniform1f(1, (GLfloat) glfwGetTime());
glUniformMatrix4fv(2, 1, false, proj.data()); glUniformMatrix4fv(2, 1, false, proj.data());
glUniformMatrix4fv(3, 1, false, state.rot.data()); glUniformMatrix4fv(3, 1, false, state.rot.data());
glDrawElements(GL_TRIANGLES, (GLsizei) dynamic.cells().size(), GL_UNSIGNED_INT, nullptr); glDrawElements(GL_TRIANGLES, elements, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0); glBindVertexArray(0);
glUseProgram(0); glUseProgram(0);
glUseProgram(wire_pgm); glUseProgram(pgm4d);
glBindVertexArray(wire_vao); glBindVertexArray(vao4d);
glUniform4fv(0, 1, state.wf.data()); glUniform4fv(0, 1, state.wf.data());
glUniform1f(1, (GLfloat) glfwGetTime()); glUniform1f(1, (GLfloat) glfwGetTime());
glUniformMatrix4fv(2, 1, false, proj.data()); glUniformMatrix4fv(2, 1, false, proj.data());
@@ -320,8 +232,7 @@ int run(GLFWwindow *window, ImGuiContext *context) {
} }
glUniform4fv(0, 1, state.wf.data()); glUniform4fv(0, 1, state.wf.data());
glDrawElements(GL_LINES, (GLsizei) wire_dynamic.cells().size(), GL_UNSIGNED_INT, nullptr); glDrawElements(GL_LINES, elements4d, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0); glBindVertexArray(0);
glUseProgram(0); glUseProgram(0);
@@ -329,14 +240,6 @@ int run(GLFWwindow *window, ImGuiContext *context) {
glfwSwapBuffers(window); glfwSwapBuffers(window);
} }
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ibo);
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &wire_vbo);
glDeleteBuffers(1, &wire_ibo);
glDeleteVertexArrays(1, &wire_vao);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }