From 916e9a89069ac74cb841e662d990a1acbdef5732 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Sat, 10 Oct 2020 22:59:51 -0400 Subject: [PATCH] Migrate to NanoGUI / Eigen for GUI and linear algebra. Also introduce a GUI play/pause button. --- CMakeLists.txt | 4 +- vendor/cgl | 1 - vis/CMakeLists.txt | 8 +- vis/include/cgl/buffer.hpp | 78 +++++++ vis/include/cgl/error.hpp | 28 +++ vis/include/cgl/pipeline.hpp | 93 ++++++++ vis/include/cgl/program.hpp | 65 ++++++ vis/include/cgl/shader.hpp | 80 +++++++ vis/include/cgl/shaderprogram.hpp | 46 ++++ vis/include/cgl/vertexarray.hpp | 75 +++++++ vis/include/mirror.hpp | 139 ++---------- vis/include/util.hpp | 8 +- vis/src/main-gui.cpp | 346 +++++++++++++----------------- 13 files changed, 640 insertions(+), 331 deletions(-) delete mode 160000 vendor/cgl create mode 100644 vis/include/cgl/buffer.hpp create mode 100644 vis/include/cgl/error.hpp create mode 100644 vis/include/cgl/pipeline.hpp create mode 100644 vis/include/cgl/program.hpp create mode 100644 vis/include/cgl/shader.hpp create mode 100644 vis/include/cgl/shaderprogram.hpp create mode 100644 vis/include/cgl/vertexarray.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3dbf3e5..db570f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,13 @@ set(CMAKE_CXX_STANDARD 17) add_subdirectory(vendor/toddcox) add_subdirectory(vendor/yaml-cpp) -add_subdirectory(vendor/cgl) -#add_subdirectory(vendor/glfw) set(NANOGUI_BUILD_EXAMPLE OFF CACHE BOOL " " FORCE) set(NANOGUI_BUILD_PYTHON OFF CACHE BOOL " " FORCE) set(NANOGUI_INSTALL OFF CACHE BOOL " " FORCE) +set(NANOGUI_USE_GLAD ON CACHE BOOL " " FORCE) add_subdirectory(vendor/nanogui) +set_property(TARGET glfw glfw_objects nanogui PROPERTY FOLDER "dependencies") add_subdirectory(vis) diff --git a/vendor/cgl b/vendor/cgl deleted file mode 160000 index a879278..0000000 --- a/vendor/cgl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a879278dd02c63746331fa6b31888fb4f8910bfe diff --git a/vis/CMakeLists.txt b/vis/CMakeLists.txt index 3f204e3..e5b21a2 100644 --- a/vis/CMakeLists.txt +++ b/vis/CMakeLists.txt @@ -12,12 +12,10 @@ add_custom_command( COMMENT "copied preses" ) -add_executable(vis src/main.cpp) -target_include_directories(vis PRIVATE include) -target_link_libraries(vis PRIVATE tc glfw yaml-cpp cgl glad) -add_dependencies(vis shaders presets) +add_definitions(${NANOGUI_EXTRA_DEFS}) +include_directories(${NANOGUI_EXTRA_INCS}) add_executable(vis-gui src/main-gui.cpp) target_include_directories(vis-gui PRIVATE include) -target_link_libraries(vis-gui PRIVATE tc nanogui) +target_link_libraries(vis-gui PRIVATE tc nanogui yaml-cpp ${NANOGUI_EXTRA_LIBS}) add_dependencies(vis-gui shaders presets) diff --git a/vis/include/cgl/buffer.hpp b/vis/include/cgl/buffer.hpp new file mode 100644 index 0000000..f2d7e8b --- /dev/null +++ b/vis/include/cgl/buffer.hpp @@ -0,0 +1,78 @@ +#pragma once + +#include + +#include + +namespace cgl { + template + class Buffer { + GLuint id{}; + + public: + Buffer() { + glCreateBuffers(1, &id); + } + + Buffer(const T &data, GLenum usage = GL_STATIC_DRAW) + : Buffer() { + put(data, usage); + } + + Buffer(const std::vector &data, GLenum usage = GL_STATIC_DRAW) + : Buffer() { + put(data, usage); + } + + Buffer(const Buffer &) = delete; + + Buffer(Buffer &&o) noexcept { + id = std::exchange(o.id, 0); + }; + + ~Buffer() { + glDeleteBuffers(1, &id); + id = 0; + } + + operator GLuint() const { + return id; + } + + [[nodiscard]] size_t size() const { + GLint res; + glGetNamedBufferParameteriv(id, GL_BUFFER_SIZE, &res); + return (size_t) res; + } + + [[nodiscard]] size_t count() const { + return size() / sizeof(T); + } + + void put(const T &data, GLenum usage = GL_STATIC_DRAW) { + glNamedBufferData(id, sizeof(T), &data, usage); + } + + void put(const std::vector &data, GLenum usage = GL_STATIC_DRAW) { + glNamedBufferData(id, sizeof(T) * data.size(), &data[0], usage); + } + + void bound(GLenum target, const std::function &action) const { + glBindBuffer(target, id); + action(); + glBindBuffer(target, 0); + } + + std::vector getSubData(size_t offset, size_t count) const { + GLintptr glOffset = offset * sizeof(T); + GLsizeiptr glSize = count * sizeof(T); + + std::vector data(count); + + glad_glGetNamedBufferSubData(id, glOffset, glSize, data.data()); + + return data; + } + }; + +} diff --git a/vis/include/cgl/error.hpp b/vis/include/cgl/error.hpp new file mode 100644 index 0000000..1a58cc1 --- /dev/null +++ b/vis/include/cgl/error.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include + +namespace cgl { + class GlError : public std::domain_error { + public: + explicit GlError(const std::string &arg) : domain_error(arg) {} + + explicit GlError(const char *string) : domain_error(string) {} + }; + + class ShaderError : public GlError { + public: + explicit ShaderError(const std::string &arg) : GlError(arg) {} + + explicit ShaderError(const char *string) : GlError(string) {} + }; + + class ProgramError : public GlError { + public: + explicit ProgramError(const std::string &arg) : GlError(arg) {} + + explicit ProgramError(const char *string) : GlError(string) {} + }; +} \ No newline at end of file diff --git a/vis/include/cgl/pipeline.hpp b/vis/include/cgl/pipeline.hpp new file mode 100644 index 0000000..172b8b2 --- /dev/null +++ b/vis/include/cgl/pipeline.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +#include + +namespace cgl { + class pipeline { + protected: + GLuint id{}; + + public: + pipeline() { + glCreateProgramPipelines(1, &id); + } + + pipeline(pipeline &) = delete; + + pipeline(pipeline &&o) noexcept { + id = std::exchange(o.id, 0); + } + + ~pipeline() { + glDeleteProgramPipelines(1, &id); + id = 0; + } + + operator GLuint() const { + return id; + } + + [[nodiscard]] int get(GLenum pname) const { + GLint res; + glGetProgramPipelineiv(id, pname, &res); + return (int) res; + } + + [[nodiscard]] std::string get_info_log() const { + auto len = (size_t) get(GL_INFO_LOG_LENGTH); + char buffer[len]; + glGetProgramPipelineInfoLog(id, len, nullptr, buffer); + return std::string(buffer); + } + + pipeline &unstage(GLenum stage_bits) { + glUseProgramStages(id, stage_bits, 0); + return *this; + } + + pipeline &stage(const ShaderProgram &pgm) { + glUseProgramStages(id, GL_VERTEX_SHADER_BIT, pgm); + return *this; + } + + pipeline &stage(const ShaderProgram &pgm) { + glUseProgramStages(id, GL_TESS_CONTROL_SHADER_BIT, pgm); + return *this; + } + + pipeline &stage(const ShaderProgram &pgm) { + glUseProgramStages(id, GL_TESS_EVALUATION_SHADER_BIT, pgm); + return *this; + } + + pipeline &stage(const ShaderProgram &pgm) { + glUseProgramStages(id, GL_GEOMETRY_SHADER_BIT, pgm); + return *this; + } + + pipeline &stage(const ShaderProgram &pgm) { + glUseProgramStages(id, GL_FRAGMENT_SHADER_BIT, pgm); + return *this; + } + + pipeline &stage(const ShaderProgram &pgm) { + glUseProgramStages(id, GL_COMPUTE_SHADER_BIT, pgm); + return *this; + } + + void bound(const std::function &action) const { + glBindProgramPipeline(id); + action(); + glBindProgramPipeline(0); + } + }; +} \ No newline at end of file diff --git a/vis/include/cgl/program.hpp b/vis/include/cgl/program.hpp new file mode 100644 index 0000000..19e5aae --- /dev/null +++ b/vis/include/cgl/program.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +#include + +namespace cgl { + class Program { + protected: + GLuint id{}; + + public: + Program() { + id = glCreateProgram(); + } + + Program(Program &) = delete; + + Program(Program &&o) noexcept { + id = std::exchange(o.id, 0); + }; + + ~Program() { + glDeleteProgram(id); + } + + operator GLuint() const { + return id; + } + + [[nodiscard]] int get(GLenum pname) const { + GLint res; + glGetProgramiv(id, pname, &res); + return (int) res; + } + + [[nodiscard]] std::string get_info_log() const { + auto len = (size_t) get(GL_INFO_LOG_LENGTH); + char buffer[len]; + glGetProgramInfoLog(id, len, nullptr, buffer); + return std::string(buffer); + } + + template + void attach(const Shader &sh) { + glAttachShader(id, sh); + } + + template + void detach(const Shader &sh) { + glDetachShader(id, sh); + } + + bool link() { + glLinkProgram(id); + return (bool) get(GL_LINK_STATUS); + } + }; +} \ No newline at end of file diff --git a/vis/include/cgl/shader.hpp b/vis/include/cgl/shader.hpp new file mode 100644 index 0000000..56bcb87 --- /dev/null +++ b/vis/include/cgl/shader.hpp @@ -0,0 +1,80 @@ +#pragma once + +#include +#include + +#include + +#include + +#include + +namespace cgl { + template + class Shader { + protected: + GLuint id{}; + + public: + Shader() { + id = glCreateShader(mode); + } + + Shader(const std::string &src) : Shader() { + set_source(src); + + if (!compile()) + throw ShaderError(get_info_log()); + } + + static Shader file(const std::string &name) { + return Shader(utilReadFile(name)); + } + + Shader(Shader &) = delete; + + Shader(Shader &&o) noexcept { + id = std::exchange(o.id, 0); + }; + + ~Shader() { + glDeleteShader(id); + } + + operator GLuint() const { + return id; + } + + [[nodiscard]] int get(GLenum pname) const { + GLint res; + glGetShaderiv(id, pname, &res); + return (int) res; + } + + [[nodiscard]] std::string get_info_log() const { + auto len = (size_t) get(GL_INFO_LOG_LENGTH); + char buffer[len]; + glGetShaderInfoLog(id, len, nullptr, buffer); + return std::string(buffer); + } + + void set_source(const std::string &src) { + const char *c_src = src.c_str(); + glShaderSource(id, 1, &c_src, nullptr); + } + + bool compile() { + glCompileShader(id); + return (bool) get(GL_COMPILE_STATUS); + } + }; + + namespace sh { + using vert = Shader; + using tcs = Shader; + using tes = Shader; + using geom = Shader; + using frag = Shader; + using comp = Shader; + } +} diff --git a/vis/include/cgl/shaderprogram.hpp b/vis/include/cgl/shaderprogram.hpp new file mode 100644 index 0000000..9f9d64d --- /dev/null +++ b/vis/include/cgl/shaderprogram.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include + +#include +#include +#include + +#include + +namespace cgl{ + template + class ShaderProgram : public Program { + public: + ShaderProgram() : Program() { + glProgramParameteri(id, GL_PROGRAM_SEPARABLE, GL_TRUE); + } + + ShaderProgram(const std::string &src) : ShaderProgram() { + Shader sh(src); + + attach(sh); + + if (!link()) + throw ShaderError(get_info_log()); + + detach(sh); + } + + static ShaderProgram file(const std::string &name) { + return ShaderProgram(utilReadFile(name)); + } + }; + + namespace pgm { + using vert = ShaderProgram; + using tcs = ShaderProgram; + using tes = ShaderProgram; + using geom = ShaderProgram; + using frag = ShaderProgram; + using comp = ShaderProgram; + } +} \ No newline at end of file diff --git a/vis/include/cgl/vertexarray.hpp b/vis/include/cgl/vertexarray.hpp new file mode 100644 index 0000000..a68ad7d --- /dev/null +++ b/vis/include/cgl/vertexarray.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +#include + +#include +#include + +namespace cgl { + class VertexArray { + GLuint id{}; + + public: + VertexArray() { + glCreateVertexArrays(1, &id); + } + + VertexArray(VertexArray &) = delete; + + VertexArray(VertexArray &&o) noexcept { + id = std::exchange(o.id, 0); + } + + ~VertexArray() { + glDeleteVertexArrays(1, &id); + id = 0; + } + + operator GLuint() const { + return id; + } + + void bound(const std::function &action) const { + glBindVertexArray(id); + action(); + glBindVertexArray(0); + } + + template + void pointer( + GLuint index, + const Buffer &buf, + unsigned size, + GLenum type, + bool normalized = false, + unsigned stride = 0 + ) const { + bound([&]() { + glEnableVertexAttribArray(index); + buf.bound(GL_ARRAY_BUFFER, [&]() { + glVertexAttribPointer(index, size, type, normalized, stride, nullptr); + }); + }); + } + + template + void ipointer( + GLuint index, + const Buffer &buf, + unsigned size, + GLenum type, + unsigned stride = 0 + ) const { + bound([&]() { + glEnableVertexAttribArray(index); + buf.bound(GL_ARRAY_BUFFER, [&]() { + glVertexAttribIPointer(index, size, type, stride, nullptr); + }); + }); + } + }; +} \ No newline at end of file diff --git a/vis/include/mirror.hpp b/vis/include/mirror.hpp index c8f8003..4affe75 100644 --- a/vis/include/mirror.hpp +++ b/vis/include/mirror.hpp @@ -6,8 +6,12 @@ #include #include -template -using vec = std::array; +#include + +template +using vec = Eigen::Matrix; +template +using mat = Eigen::Matrix; using vec1 = vec<1>; using vec2 = vec<2>; @@ -15,75 +19,12 @@ using vec3 = vec<3>; using vec4 = vec<4>; using vec5 = vec<5>; -template -using mat = std::array, N>; - using mat1 = mat<1>; using mat2 = mat<2>; using mat3 = mat<3>; using mat4 = mat<4>; using mat5 = mat<5>; -template -V operator*(V a, const float &b) { - for (auto &e : a) e *= b; - return a; -} - -template -V operator*(const float &b, V a) { - for (auto &e : a) e *= b; - return a; -} - -template -V operator/(V a, const float &b) { - for (auto &e : a) e /= b; - return a; -} - -template -V operator+(const V &a, V b) { - for (int i = 0; i < a.size(); ++i) { - a[i] += b[i]; - } - return a; -} - -template -V operator-(V a, const V &b) { - for (int i = 0; i < a.size(); ++i) { - a[i] -= b[i]; - } - return a; -} - -template -void operator-=(V &a, const V &b) { - for (int i = 0; i < a.size(); ++i) { - a[i] -= b[i]; - } -} - -template -void operator+=(V &a, const V &b) { - for (int i = 0; i < a.size(); ++i) { - a[i] += b[i]; - } -} - -template -float length(const V &a) { - float sum = 0; - for (const auto &e : a) sum += e * e; - return sqrtf(sum); -} - -template -V normalized(const V &a) { - return a / length(a); -} - template float dot(int n, const V &a, const V &b) { float sum = 0; @@ -93,39 +34,6 @@ float dot(int n, const V &a, const V &b) { return sum; } -template -float dot(const V &a, const V &b) { - float sum = 0; - for (int i = 0; i < a.size(); ++i) { - sum += a[i] * b[i]; - } - return sum; -} - -vec5 mul(vec5 v, mat5 m) { - vec5 r{}; - - for (int i = 0; i < 5; ++i) - for (int j = 0; j < 5; ++j) - r[i] += m[i][j] * v[j]; - - return r; -} - -mat5 mul(mat5 a, mat5 b) { - mat5 r{}; - - for (int i = 0; i < 5; ++i) { - for (int j = 0; j < 5; ++j) { - for (int k = 0; k < 5; ++k) { - r[i][j] += a[i][k] * b[k][j]; - } - } - } - - return r; -} - template std::vector> mirror(const tc::Group &group) { std::vector> mirrors; @@ -152,7 +60,7 @@ std::vector> mirror(const tc::Group &group) { std::vector> res; for (const auto &v : mirrors) { - vec rv{}; + vec rv = vec::Zero(); // ortho proj for (int i = 0; i < std::min(v.size(), (size_t) N); ++i) { @@ -184,7 +92,7 @@ vec ortho(const vec &v) { template V project(const V &vec, const V &target) { - return dot(vec, target) / dot(target, target) * target; + return vec.dot(target) / target.dot(target) * target; } template @@ -200,14 +108,14 @@ V gram_schmidt_last(std::vector vecs) { } } - return normalized(vecs[vecs.size() - 1]); + return vecs[vecs.size() - 1].normalized(); } template V barycentric(const std::vector &basis, const C &coords) { - V res{}; + V res = V::Zero(); - int N = std::min(basis.size(), coords.size()); + int N = std::min((int) basis.size(), (int) coords.rows()); for (int i = 0; i < N; ++i) { res += basis[i] * coords[i]; } @@ -226,29 +134,22 @@ std::vector plane_intersections(std::vector normals) { return results; } -template -mat identity() { - mat res{}; - for (int i = 0; i < N; ++i) - res[i][i] = 1; - return res; -} - template mat rot(int u, int v, float theta) { - auto res = identity(); - res[u][u] = std::cos(theta); - res[u][v] = std::sin(theta); - res[v][u] = -std::sin(theta); - res[v][v] = std::cos(theta); + mat res = mat::Identity(); + res(u, u) = std::cos(theta); + res(u, v) = std::sin(theta); + res(v, u) = -std::sin(theta); + res(v, v) = std::cos(theta); return res; } mat4 ortho(float left, float right, float bottom, float top, float front, float back) { - return { + mat<4> res = mat4(); + res << 2 / (right - left), 0, 0, -(right + left) / (right - left), 0, 2 / (top - bottom), 0, -(top + bottom) / (top - bottom), 0, 0, 2 / (front - back), -(front + back) / (front - back), - 0, 0, 0, 1, - }; + 0, 0, 0, 1; + return res; } \ No newline at end of file diff --git a/vis/include/util.hpp b/vis/include/util.hpp index efef3a7..3bb9e0c 100644 --- a/vis/include/util.hpp +++ b/vis/include/util.hpp @@ -1,13 +1,13 @@ #pragma once +#include + #include #include #include #include #include -#include - std::string utilInfo() { std::stringstream ss; ss @@ -19,6 +19,10 @@ std::string utilInfo() { return ss.str(); } +std::string utilGetString(GLenum name) { + return reinterpret_cast(glGetString(name)); +} + std::string utilReadFile(const std::string &filename) { std::ifstream in(filename, std::ios::in | std::ios::binary); if (in) { diff --git a/vis/src/main-gui.cpp b/vis/src/main-gui.cpp index 20b116d..83750ca 100644 --- a/vis/src/main-gui.cpp +++ b/vis/src/main-gui.cpp @@ -12,236 +12,178 @@ */ #include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include + #include #include -// Includes for the GLTexture class. -#include -#include -#include +#include +#include +#include +#include +#include +#include -#if defined(__GNUC__) -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif -#if defined(_WIN32) -# pragma warning(push) -# pragma warning(disable: 4457 4456 4005 4312) -#endif +struct Matrices { + mat4 proj = mat4::Identity(); + mat4 view = mat4::Identity(); -#if defined(_WIN32) -# pragma warning(pop) -#endif -#if defined(_WIN32) -# if defined(APIENTRY) -# undef APIENTRY -# endif -# include -#endif + Matrices() = default; -using std::cout; -using std::cerr; -using std::endl; -using std::string; -using std::vector; -using std::pair; -using std::to_string; + Matrices(mat4 proj, mat4 view) : proj(std::move(proj)), view(std::move(view)) {} + static Matrices build(const nanogui::Screen &screen) { + auto aspect = (float) screen.width() / (float) screen.height(); + auto pheight = 1.4f; + auto pwidth = aspect * pheight; + mat4 proj = ortho(-pwidth, pwidth, -pheight, pheight, -10.0f, 10.0f); -class MyGLCanvas : public nanogui::GLCanvas { -public: - MyGLCanvas(Widget *parent) : nanogui::GLCanvas(parent), mRotation(nanogui::Vector3f(0.25f, 0.5f, 0.33f)) { - using namespace nanogui; +// if (!glfwGetKey(window, GLFW_KEY_LEFT_SHIFT)) { +// state.st += state.time_delta / 8; +// } - mShader.init( - /* An identifying name */ - "a_simple_shader", - - /* Vertex shader */ - "#version 330\n" - "uniform mat4 modelViewProj;\n" - "in vec3 position;\n" - "in vec3 color;\n" - "out vec4 frag_color;\n" - "void main() {\n" - " frag_color = 3.0 * modelViewProj * vec4(color, 1.0);\n" - " gl_Position = modelViewProj * vec4(position, 1.0);\n" - "}", - - /* Fragment shader */ - "#version 330\n" - "out vec4 color;\n" - "in vec4 frag_color;\n" - "void main() {\n" - " color = frag_color;\n" - "}" - ); - - MatrixXu indices(3, 12); /* Draw a cube */ - indices.col( 0) << 0, 1, 3; - indices.col( 1) << 3, 2, 1; - indices.col( 2) << 3, 2, 6; - indices.col( 3) << 6, 7, 3; - indices.col( 4) << 7, 6, 5; - indices.col( 5) << 5, 4, 7; - indices.col( 6) << 4, 5, 1; - indices.col( 7) << 1, 0, 4; - indices.col( 8) << 4, 0, 3; - indices.col( 9) << 3, 7, 4; - indices.col(10) << 5, 6, 2; - indices.col(11) << 2, 1, 5; - - MatrixXf positions(3, 8); - positions.col(0) << -1, 1, 1; - positions.col(1) << -1, 1, -1; - positions.col(2) << 1, 1, -1; - positions.col(3) << 1, 1, 1; - positions.col(4) << -1, -1, 1; - positions.col(5) << -1, -1, -1; - positions.col(6) << 1, -1, -1; - positions.col(7) << 1, -1, 1; - - MatrixXf colors(3, 12); - colors.col( 0) << 1, 0, 0; - colors.col( 1) << 0, 1, 0; - colors.col( 2) << 1, 1, 0; - colors.col( 3) << 0, 0, 1; - colors.col( 4) << 1, 0, 1; - colors.col( 5) << 0, 1, 1; - colors.col( 6) << 1, 1, 1; - colors.col( 7) << 0.5, 0.5, 0.5; - colors.col( 8) << 1, 0, 0.5; - colors.col( 9) << 1, 0.5, 0; - colors.col(10) << 0.5, 1, 0; - colors.col(11) << 0.5, 1, 0.5; - - mShader.bind(); - mShader.uploadIndices(indices); - - mShader.uploadAttrib("position", positions); - mShader.uploadAttrib("color", colors); - } - - ~MyGLCanvas() { - mShader.free(); - } - - void setRotation(nanogui::Vector3f vRotation) { - mRotation = vRotation; - } - - virtual void drawGL() override { - using namespace nanogui; - - mShader.bind(); - - Matrix4f mvp; - mvp.setIdentity(); - float fTime = (float)glfwGetTime(); - mvp.topLeftCorner<3,3>() = Eigen::Matrix3f(Eigen::AngleAxisf(mRotation[0]*fTime, Vector3f::UnitX()) * - Eigen::AngleAxisf(mRotation[1]*fTime, Vector3f::UnitY()) * - Eigen::AngleAxisf(mRotation[2]*fTime, Vector3f::UnitZ())) * 0.25f; - - mShader.setUniform("modelViewProj", mvp); - - glEnable(GL_DEPTH_TEST); - /* Draw 12 triangles starting at index 0 */ - mShader.drawIndexed(GL_TRIANGLES, 0, 12); - glDisable(GL_DEPTH_TEST); - } - -private: - nanogui::GLShader mShader; - Eigen::Vector3f mRotation; + auto view = mat4::Identity(); + return Matrices(proj, view); + } }; +template +std::vector points(const tc::Group &group, const C &coords, const float time) { + auto cosets = group.solve(); + auto mirrors = mirror<5>(group); + + auto corners = plane_intersections(mirrors); + auto start = barycentric(corners, coords); + + auto higher = cosets.path.walk(start, mirrors, reflect); + + mat5 r = mat5::Identity(); + r *= rot<5>(0, 2, time * .21f); +// r *= rot<5>(1, 4, time * .27f); + + r *= rot<5>(0, 3, time * .17f); + r *= rot<5>(1, 3, time * .25f); + r *= rot<5>(2, 3, time * .12f); + + std::transform(higher.begin(), higher.end(), higher.begin(), [&](vec5 v) { return r * v; }); + + std::vector lower(higher.size()); + std::transform(higher.begin(), higher.end(), lower.begin(), stereo<4>); + return lower; +} + +template +Prop<4, vec4> make_slice( + const tc::Group &g, + const C &coords, + vec3 color, + T all_sg_gens, + const std::vector> &exclude +) { + Prop res{}; + +// res.vbo.put(points(g, coords)); + res.ibo.put(merge(hull(g, all_sg_gens, exclude))); + res.vao.ipointer(0, res.ibo, 4, GL_UNSIGNED_INT); + + return res; +} class ExampleApplication : public nanogui::Screen { public: - ExampleApplication() : nanogui::Screen(Eigen::Vector2i(800, 600), "NanoGUI Test", false) { - using namespace nanogui; + vec5 root; + std::unique_ptr group; + std::unique_ptr> prop; + std::unique_ptr> ubo; + std::unique_ptr> ren; - Window *window = new Window(this, "GLCanvas Demo"); - window->setPosition(Vector2i(15, 15)); - window->setLayout(new GroupLayout()); + float glfw_time = 0; + float last_frame = 0; + float frame_time = 0; + float time = 0; - mCanvas = new MyGLCanvas(window); - mCanvas->setBackgroundColor({100, 100, 100, 255}); - mCanvas->setSize({400, 400}); + bool paused = false; - Widget *tools = new Widget(window); - tools->setLayout(new BoxLayout(Orientation::Horizontal, - Alignment::Middle, 0, 5)); + ExampleApplication() : nanogui::Screen( + Eigen::Vector2i(1920, 1080), + "Coset Visualization", + true, false, + 8, 8, 24, 8, + 4, + 4, 5) { + using namespace nanogui; - Button *b0 = new Button(tools, "Random Color"); - b0->setCallback([this]() { mCanvas->setBackgroundColor(Vector4i(rand() % 256, rand() % 256, rand() % 256, 255)); }); + Window *window = new Window(this, "Sample Window"); + window->setPosition(Vector2i(15, 15)); + window->setFixedWidth(250); + window->setLayout(new BoxLayout(Orientation::Vertical)); - Button *b1 = new Button(tools, "Random Rotation"); - b1->setCallback([this]() { mCanvas->setRotation(nanogui::Vector3f((rand() % 100) / 100.0f, (rand() % 100) / 100.0f, (rand() % 100) / 100.0f)); }); + auto pause = new ToolButton(window, ENTYPO_ICON_CONTROLLER_PAUS); + pause->setFlags(Button::ToggleButton); + pause->setChangeCallback([&](bool value) { this->paused = value; }); - performLayout(); - } + performLayout(); - virtual bool keyboardEvent(int key, int scancode, int action, int modifiers) { - if (Screen::keyboardEvent(key, scancode, action, modifiers)) - return true; - if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { - setVisible(false); - return true; + std::cout << utilInfo(); + + std::vector symbol = {5, 3, 3, 2}; + root << .80, .02, .02, .02, .02; + + group = std::make_unique(tc::schlafli(symbol)); + auto gens = generators(*group); + std::vector> exclude = {{0, 1, 2}}; + auto combos = Combos(gens, 3); + + prop = std::make_unique>(make_slice<4>(*group, root, {}, combos, exclude)); + + ubo = std::make_unique>(); + glBindBufferBase(GL_UNIFORM_BUFFER, 1, *ubo); + + ren = std::make_unique>(); } - return false; - } - virtual void draw(NVGcontext *ctx) { - /* Draw the user interface */ - Screen::draw(ctx); - } -private: - MyGLCanvas *mCanvas; + void drawContents() override { + glEnable(GL_DEPTH_TEST); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + glViewport(0, 0, width(), height()); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glfw_time = (float) glfwGetTime(); + frame_time = glfw_time - last_frame; + last_frame = glfw_time; + if (!paused) time += frame_time; + + std::get<0>(prop->vbos).put(points(*group, root, time)); + + Matrices mats = Matrices::build(*this); + ubo->put(mats); + ren->draw(*prop); + } }; int main(int /* argc */, char ** /* argv */) { - try { - nanogui::init(); + try { + nanogui::init(); - /* scoped variables */ { - nanogui::ref app = new ExampleApplication(); - app->drawAll(); - app->setVisible(true); - nanogui::mainloop(); + /* scoped variables */ { + nanogui::ref app = new ExampleApplication(); + app->drawAll(); + app->setVisible(true); + nanogui::mainloop(1); + } + + nanogui::shutdown(); + } catch (const std::runtime_error &e) { + std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what()); + std::cerr << error_msg << std::endl; + return -1; } - nanogui::shutdown(); - } catch (const std::runtime_error &e) { - std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what()); -#if defined(_WIN32) - MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK); -#else - std::cerr << error_msg << endl; -#endif - return -1; - } - - return 0; + return 0; } \ No newline at end of file