diff --git a/CMakeLists.txt b/CMakeLists.txt index d39ec08..4b615b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,13 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/res ${CMAKE_CURRENT_BINARY_DIR}/res 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) add_dependencies(vis resources) diff --git a/src/gl/buffer.hpp b/src/gl/buffer.hpp new file mode 100644 index 0000000..3771443 --- /dev/null +++ b/src/gl/buffer.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include + +#include + +template +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 + 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); + } +}; diff --git a/src/gldebug.hpp b/src/gl/debug.hpp similarity index 100% rename from src/gldebug.hpp rename to src/gl/debug.hpp diff --git a/src/gl/shader.hpp b/src/gl/shader.hpp new file mode 100644 index 0000000..70111fa --- /dev/null +++ b/src/gl/shader.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include + +template +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(source), + std::istreambuf_iterator() + )) {} + + 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; +using FragmentShader = Shader; + +class Program { +private: + GLuint id = 0; + +public: + template + explicit Program(const Shader &...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; + } +}; diff --git a/src/gl/types.hpp b/src/gl/types.hpp new file mode 100644 index 0000000..7149868 --- /dev/null +++ b/src/gl/types.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include + +#include + +template +struct Buffer; + +template +struct VertexArray; + + +template +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 + using As = Format; + + static inline constexpr void apply(GLuint vao, GLuint idx) { + glEnableVertexArrayAttrib(vao, idx); + glVertexArrayAttribFormat(vao, idx, size, type, norm, offset); + } +}; + +template +struct IFormat { + using CType = ctype; + static constexpr GLenum Type = type; + static constexpr GLint Size = size; + static constexpr GLuint Offset = offset; + + template + using As = IFormat; + + static inline constexpr void apply(GLuint vao, GLuint idx) { + glEnableVertexArrayAttrib(vao, idx); + glVertexArrayAttribIFormat(vao, idx, size, type, offset); + } +}; + +template +struct LFormat { + using CType = ctype; + static constexpr GLenum Type = type; + static constexpr GLint Size = size; + static constexpr GLuint Offset = offset; + + template + using As = LFormat; + + static inline constexpr void apply(GLuint vao, GLuint idx) { + glEnableVertexArrayAttrib(vao, idx); + glVertexArrayAttribLFormat(vao, idx, size, type, offset); + } +}; + +template +struct AutoFormat { + using Fmt = Fmt_; +}; + +template<> +struct AutoFormat { + using Fmt = LFormat; +}; + +template<> +struct AutoFormat { + using Fmt = Format; +}; + +template<> +struct AutoFormat { + using Fmt = IFormat; +}; + +template +struct AutoFormat> { + static_assert((Dim >= 1) && (Dim <= 4), "Dim must be in range 1-4"); + + using Fmt = typename AutoFormat::Fmt::template As, Dim>; +}; + +template +struct Binder { + using CType = typename AutoFormat::Fmt::CType; + + const GLuint buf; + const GLuint offset; + const GLuint stride; + + Binder(const Buffer &buf) // NOLINT(google-explicit-constructor) + : buf((GLuint) buf), offset(0), stride(sizeof(CType)) {} + + template + Binder(const Buffer &buf, GLuint offset) + : buf((GLuint) buf), offset(offset), stride(sizeof(CType_)) {} + + Binder(const Binder &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(buf, offsetof(decltype(buf)::Type, field)) diff --git a/src/gl/vertexarray.hpp b/src/gl/vertexarray.hpp new file mode 100644 index 0000000..7b863e6 --- /dev/null +++ b/src/gl/vertexarray.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include + +#include "buffer.hpp" +#include "types.hpp" + +template +class VertexArray { +public: + template + using Fmt = std::tuple_element_t>; + +private: + GLuint id = 0; + + template + inline void formatall( + std::integer_sequence + ) { + (AutoFormat::Fmt::apply(id, idx), ...); + } + + template + inline void bindall( + const Binder &...buf, + std::integer_sequence + ) { + (buf.bind(id, idx), ...); + } + +public: + explicit VertexArray() { + glCreateVertexArrays(1, &id); + formatall(std::make_index_sequence()); + } + + explicit VertexArray(const Binder &...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 &...buf) { + bindall(buf..., std::make_index_sequence()); + } + + template + void bind(const Binder> &buf) { + buf.bind(id, idx); + } +}; diff --git a/src/main.cpp b/src/main.cpp index 0f66dcb..d4fa6e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,10 @@ #include #include -#include "gldebug.hpp" +#include "gl/debug.hpp" +#include "gl/buffer.hpp" +#include "gl/shader.hpp" +#include "gl/vertexarray.hpp" #include #include @@ -123,145 +126,54 @@ void set_style() { int run(GLFWwindow *window, ImGuiContext *context) { State state; + Buffer ind_buf; + Buffer vert_buf; + Buffer vert2_buf; + + VertexArray vao(vert_buf); + glVertexArrayElementBuffer(vao, ind_buf); + 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; - glCreateVertexArrays(1, &vao); + // todo add to DynamicMesh + // 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 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; - glCreateBuffers(1, &vbo); + auto mesh2 = ml::CubeMesh(0.5f); + auto vert2_data_dyn = mesh2.points(); + Eigen::Ref 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); - constexpr size_t cell_scalar_size = sizeof(ml::DynamicMesh::CellsType::Scalar); + auto mesh4d = ml::WireCubeMesh(4, 0.33f); - glNamedBufferData( - vbo, - (GLsizeiptr) (point_scalar_size * dynamic.points().size()), - dynamic.points().data(), - 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); + Buffer ind4d_buf; + Buffer vert4d_buf; + VertexArray vao4d(vert4d_buf); + glVertexArrayElementBuffer(vao4d, ind4d_buf); - GLuint ibo; - glCreateBuffers(1, &ibo); - glGetError(); - glNamedBufferData( - ibo, - (GLsizeiptr) (cell_scalar_size * dynamic.cells().size()), - dynamic.cells().data(), - GL_STATIC_DRAW - ); - glVertexArrayElementBuffer(vao, ibo); + auto ind4d_data = mesh4d.cells(); + auto ind4d_flat = ind4d_data.reshaped(); + auto elements4d = ind4d_buf.upload(ind4d_flat.begin(), ind4d_flat.end(), GL_STATIC_DRAW); - auto wire_mesh = ml::WireCubeMesh(4, 0.33f); - auto wire_dynamic = (ml::DynamicMesh) wire_mesh; + auto vert4d_data_dyn = mesh4d.points(); + Eigen::Ref 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; - glCreateVertexArrays(1, &wire_vao); + VertexShader vs(std::ifstream("res/shaders/main.vert.glsl")); + VertexShader vs4d(std::ifstream("res/shaders/4d.vert.glsl")); + FragmentShader fs(std::ifstream("res/shaders/main.frag.glsl")); - GLuint wire_vbo; - glCreateBuffers(1, &wire_vbo); - - 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(vs_file)), - std::istreambuf_iterator() - ); - 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(wire_vs_file)), - std::istreambuf_iterator() - ); - 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(fs_file)), - std::istreambuf_iterator() - ); - 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; - } + Program pgm(vs, fs); + Program pgm4d(vs4d, fs); glEnable(GL_DEPTH_TEST); @@ -291,12 +203,12 @@ int run(GLFWwindow *window, ImGuiContext *context) { glUniform1f(1, (GLfloat) glfwGetTime()); glUniformMatrix4fv(2, 1, false, proj.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); glUseProgram(0); - glUseProgram(wire_pgm); - glBindVertexArray(wire_vao); + glUseProgram(pgm4d); + glBindVertexArray(vao4d); glUniform4fv(0, 1, state.wf.data()); glUniform1f(1, (GLfloat) glfwGetTime()); glUniformMatrix4fv(2, 1, false, proj.data()); @@ -320,8 +232,7 @@ int run(GLFWwindow *window, ImGuiContext *context) { } 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); glUseProgram(0); @@ -329,14 +240,6 @@ int run(GLFWwindow *window, ImGuiContext *context) { 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; }