Serializable mesh with nlohmann_json

Displays a cube generated by meshlib.hpp (serialtest)
This commit is contained in:
David Allemang
2022-02-11 16:12:27 -05:00
committed by David Allemang
parent 10a49a4172
commit 7fff7a375f
6 changed files with 302 additions and 21 deletions

View File

@@ -9,6 +9,10 @@ include(External/glfw.cmake)
include(External/imgui.cmake) include(External/imgui.cmake)
include(External/eigen.cmake) include(External/eigen.cmake)
include(External/glad.cmake) include(External/glad.cmake)
include(External/json.cmake)
add_executable(vis main.cpp) add_executable(vis main.cpp)
target_link_libraries(vis glfw glad imgui eigen) target_link_libraries(vis glfw glad imgui eigen nlohmann_json)
add_executable(serial serialtest.cpp)
target_link_libraries(serial eigen nlohmann_json)

7
External/json.cmake vendored Normal file
View File

@@ -0,0 +1,7 @@
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.10.5
)
SET(JSON_ImplicitConversions OFF CACHE INTERNAL "")
FetchContent_MakeAvailable(json)

View File

@@ -0,0 +1,72 @@
#pragma once
#include "meshlib.hpp"
#include <Eigen/Eigen>
#include <nlohmann/json.hpp>
namespace Eigen {
template<class Derived>
void to_json(nlohmann::json &json, const Eigen::PlainObjectBase<Derived> &mat) {
using Scalar = typename Derived::Scalar;
auto rows = mat.rows();
auto cols = mat.cols();
std::vector<Scalar> vals(mat.size());
Map<Derived>(vals.data(), rows, cols) = mat;
json = {
{"rows", rows},
{"cols", cols},
{"vals", vals},
};
}
template<class Derived>
void from_json(const nlohmann::json &j, Derived &mat) {
using Scalar = typename Derived::Scalar;
auto rows = j["rows"].get<Eigen::Index>();
auto cols = j["cols"].get<Eigen::Index>();
auto vals = j["vals"].get<std::vector<Scalar>>();
mat = Eigen::Map<Derived>(vals.data(), rows, cols);
}
}
namespace ml {
static void to_json(nlohmann::json &json, const ml::MeshBase &mesh) {
json = {
{"points", mesh.points()},
{"cells", mesh.cells()},
};
}
static void from_json(const nlohmann::json &j, ml::DynamicMesh &mesh) {
mesh = {
j["points"].get<DynamicMesh::PointsType>(),
j["cells"].get<DynamicMesh::CellsType>(),
};
}
void write(const ml::MeshBase &mesh, std::ostream &out) {
nlohmann::json json = mesh;
nlohmann::json::to_msgpack(json, out);
}
void write(const ml::MeshBase &mesh, const std::string &path) {
std::ofstream file(path, std::ios::out | std::ios::binary);
write(mesh, file);
}
ml::DynamicMesh read(std::istream &in) {
nlohmann::json json = nlohmann::json::from_msgpack(in);
return json.get<ml::DynamicMesh>();
}
ml::DynamicMesh read(const std::string &path) {
std::ifstream file(path, std::ios::in | std::ios::binary);
return read(file);
}
}

97
meshlib.hpp Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#include <iostream>
#include <ostream>
#include <memory>
#include <utility>
#include <Eigen/Eigen>
namespace ml {
class MeshBase {
public:
using PointsType = Eigen::MatrixXf;
using CellsType = Eigen::MatrixXi;
[[nodiscard]] virtual PointsType points() const = 0;
[[nodiscard]] virtual CellsType cells() const = 0;
};
class DynamicMesh : public MeshBase {
PointsType _points;
CellsType _cells;
public:
DynamicMesh() = default;
explicit DynamicMesh(const MeshBase &mesh)
: _points(mesh.points()), _cells(mesh.cells()) {}
DynamicMesh(PointsType points, CellsType cells)
: _points(std::move(points)), _cells(std::move(cells)) {}
[[nodiscard]] PointsType &points() {
return _points;
}
[[nodiscard]] PointsType points() const override {
return _points;
}
[[nodiscard]] CellsType &cells() {
return _cells;
}
[[nodiscard]] CellsType cells() const override {
return _cells;
}
};
class CubeMesh : public MeshBase {
PointsType::Scalar radius;
public:
explicit CubeMesh(PointsType::Scalar radius = 0.5)
: radius(radius) {}
[[nodiscard]] PointsType points() const override {
PointsType out(3, 8);
out.transpose() <<
+radius, +radius, +radius,
+radius, +radius, -radius,
+radius, -radius, +radius,
+radius, -radius, -radius,
-radius, +radius, +radius,
-radius, +radius, -radius,
-radius, -radius, +radius,
-radius, -radius, -radius;
return out;
}
CellsType cells() const override {
CellsType out(3, 12);
out.transpose() <<
0b000, 0b001, 0b010, 0b001, 0b010, 0b011,
0b100, 0b101, 0b110, 0b101, 0b110, 0b111,
0b000, 0b001, 0b100, 0b001, 0b100, 0b101,
0b010, 0b011, 0b110, 0b011, 0b110, 0b111,
0b000, 0b010, 0b100, 0b010, 0b100, 0b110,
0b001, 0b011, 0b101, 0b011, 0b101, 0b111;
return out;
}
};
// class CubeMesh : public MeshBase<Eigen::MatrixXf, Eigen::MatrixXi> {
// public:
// const Eigen::MatrixBase<PointsType> &points() const override {
// return PointsType::Random(3, 8);
// }
//
// const Eigen::MatrixBase<CellsType> &cells() const override {
// return <#initializer#>;
// }
// };
}

View File

@@ -4,10 +4,19 @@
#include <backends/imgui_impl_glfw.h> #include <backends/imgui_impl_glfw.h>
#include <backends/imgui_impl_opengl3.h> #include <backends/imgui_impl_opengl3.h>
#include <iostream> #include <iostream>
#include <fstream>
#include "gldebug.hpp" #include "gldebug.hpp"
void show_overlay(float* clear_color) { #include "meshlib.hpp"
#include "meshlib_json.hpp"
struct State {
float bg[4] = {0.45f, 0.55f, 0.60f, 1.00f};
float fg[4] = {0.19f, 0.86f, 0.33f, 1.00f};
};
void show_overlay(State &state) {
static std::string gl_vendor = (const char *) glGetString(GL_VENDOR); static std::string gl_vendor = (const char *) glGetString(GL_VENDOR);
static std::string gl_renderer = (const char *) glGetString(GL_RENDERER); static std::string gl_renderer = (const char *) glGetString(GL_RENDERER);
static std::string gl_version = (const char *) glGetString(GL_VERSION); static std::string gl_version = (const char *) glGetString(GL_VERSION);
@@ -48,7 +57,7 @@ void show_overlay(float* clear_color) {
ImGui::Separator(); ImGui::Separator();
ImGui::ColorEdit3("Background", state.bg, ImGuiColorEditFlags_Float); ImGui::ColorEdit3("Background", state.bg, ImGuiColorEditFlags_Float);
ImGui::SliderFloat("Alpha", &state.fg[3], 0.0f, 1.0f, "%.2f"); ImGui::ColorEdit3("Foreground", state.fg, ImGuiColorEditFlags_Float);
ImGui::End(); ImGui::End();
} }
@@ -66,17 +75,10 @@ void set_style() {
int run(GLFWwindow *window, ImGuiContext *context) { int run(GLFWwindow *window, ImGuiContext *context) {
State state; State state;
float points[]{ auto mesh = ml::CubeMesh(0.5f);
+0.5f, +0.5f, 0.0f, 1.0f, // auto mesh = ml::read("circle.pak");
+0.5f, -0.5f, 0.0f, 1.0f,
-0.5f, +0.5f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
};
unsigned int inds[]{ auto dynamic = (ml::DynamicMesh) mesh;
0, 1, 2,
1, 2, 3,
};
GLuint vao; GLuint vao;
glCreateVertexArrays(1, &vao); glCreateVertexArrays(1, &vao);
@@ -84,21 +86,55 @@ int run(GLFWwindow *window, ImGuiContext *context) {
GLuint vbo; GLuint vbo;
glCreateBuffers(1, &vbo); glCreateBuffers(1, &vbo);
glNamedBufferData(vbo, sizeof(points), (void *) points, 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);
glNamedBufferData(
vbo,
(GLsizeiptr) (point_scalar_size * dynamic.points().size()),
dynamic.points().data(),
GL_STATIC_DRAW
);
glEnableVertexArrayAttrib(vao, 0); glEnableVertexArrayAttrib(vao, 0);
glVertexArrayVertexBuffer(vao, 0, vbo, 0, sizeof(float) * 4); glVertexArrayVertexBuffer(vao, 0,
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0); vbo, 0,
(GLsizeiptr) (point_scalar_size * dynamic.points().rows())
);
glVertexArrayAttribFormat(vao, 0, 3, GL_FLOAT, GL_FALSE, 0);
GLuint ibo; GLuint ibo;
glCreateBuffers(1, &ibo); glCreateBuffers(1, &ibo);
glGetError(); glGetError();
glNamedBufferData(ibo, sizeof(inds), (void *) inds, GL_STATIC_DRAW); glNamedBufferData(
ibo,
(GLsizeiptr) (cell_scalar_size * dynamic.cells().size()),
dynamic.cells().data(),
GL_STATIC_DRAW
);
glVertexArrayElementBuffer(vao, ibo); glVertexArrayElementBuffer(vao, ibo);
const char *vs_src = "#version 440\n" const char *vs_src = "#version 440\n"
"layout(location=0) in vec4 pos;" "layout(location=1) uniform float time;"
"layout(location=2) uniform mat4 proj;"
"layout(location=0) in vec3 pos;"
"void main() {" "void main() {"
" gl_Position = pos;" " float c2 = cos(time * 0.2);"
" float s2 = sin(time * 0.2);"
" float c3 = cos(time * 0.3);"
" float s3 = sin(time * 0.3);"
" mat4 r1 = mat4("
" c2, -s2, 0.0, 0.0,"
" s2, c2, 0.0, 0.0,"
" 0.0, 0.0, 1.0, 0.0,"
" 0.0, 0.0, 0.0, 1.0"
" );"
" mat4 r2 = mat4("
" c3, 0.0, -s3, 0.0,"
" 0.0, 1.0, 0.0, 0.0,"
" s3, 0.0, c3, 0.0,"
" 0.0, 0.0, 0.0, 1.0"
");"
" gl_Position = proj * r2 * r1 * vec4(pos, 1.0);"
"}"; "}";
GLuint vs = glCreateShader(GL_VERTEX_SHADER); GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &vs_src, nullptr); glShaderSource(vs, 1, &vs_src, nullptr);
@@ -108,7 +144,10 @@ int run(GLFWwindow *window, ImGuiContext *context) {
"layout(location=0) uniform vec4 ucol;" "layout(location=0) uniform vec4 ucol;"
"layout(location=0) out vec4 col;" "layout(location=0) out vec4 col;"
"void main() {" "void main() {"
" float d = 1.0 - gl_FragCoord.z;"
" d = (d - 0.5) / 0.7 + 0.5;"
" col = ucol;" " col = ucol;"
" col.xyz *= d;"
"}"; "}";
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &fs_src, nullptr); glShaderSource(fs, 1, &fs_src, nullptr);
@@ -119,6 +158,10 @@ int run(GLFWwindow *window, ImGuiContext *context) {
glAttachShader(pgm, fs); glAttachShader(pgm, fs);
glLinkProgram(pgm); glLinkProgram(pgm);
glEnable(GL_DEPTH_TEST);
Eigen::Projective3f proj;
while (!glfwWindowShouldClose(window)) { while (!glfwWindowShouldClose(window)) {
glfwPollEvents(); glfwPollEvents();
@@ -132,12 +175,17 @@ int run(GLFWwindow *window, ImGuiContext *context) {
glfwGetFramebufferSize(window, &display_w, &display_h); glfwGetFramebufferSize(window, &display_w, &display_h);
glViewport(0, 0, display_w, display_h); glViewport(0, 0, display_w, display_h);
glClearColor(state.bg[0], state.bg[1], state.bg[2], state.bg[3]); glClearColor(state.bg[0], state.bg[1], state.bg[2], state.bg[3]);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
auto aspect = (float) display_h / (float) display_w;
proj = Eigen::AlignedScaling3f(aspect, 1.0, -1.0);
glUseProgram(pgm); glUseProgram(pgm);
glBindVertexArray(vao); glBindVertexArray(vao);
glUniform4fv(0, 1, state.fg); glUniform4fv(0, 1, state.fg);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); glUniform1f(1, (GLfloat) glfwGetTime());
glUniformMatrix4fv(2, 1, false, proj.data());
glDrawElements(GL_TRIANGLES, (GLsizei) dynamic.cells().size(), GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0); glBindVertexArray(0);
glUseProgram(0); glUseProgram(0);

53
src/serialtest.cpp Normal file
View File

@@ -0,0 +1,53 @@
#include <Eigen/Eigen>
#include <fstream>
#include <iostream>
#include "meshlib.hpp"
#include "meshlib_json.hpp"
class Circle : public ml::MeshBase {
public:
PointsType::Scalar radius;
Circle(PointsType::Scalar radius) : radius(radius) {}
PointsType points() const override {
PointsType t(1, 32);
for (int i = 0; i < t.size(); ++i) {
t(i) = 6.28318f * (float) i / 32;
}
PointsType out(3, 32);
out.array().row(0) = t.array().sin();
out.array().row(1) = t.array().cos();
out.array().row(2).setZero();
return out;
}
CellsType cells() const override {
CellsType t(1, 31);
for (int i = 0; i < t.size(); ++i) {
t(i) = i;
}
CellsType out(3, 31);
out.array().row(0) = 0;
out.array().row(1) = t.array();
out.array().row(2) = t.array() + 1;
return out;
}
};
int main() {
auto omesh = Circle(1.0f);
ml::write(omesh, "circle.pak");
auto imesh = ml::read("circle.pak");
std::cout << "= points ===============" << std::endl;
std::cout << imesh.points() << std::endl;
std::cout << "= cells ================" << std::endl;
std::cout << imesh.cells() << std::endl;
}