|
|
@@ -0,0 +1,330 @@
|
|
|
+#include "mesh_render.h"
|
|
|
+#include "render_utility.h"
|
|
|
+#include "texture_render.h"
|
|
|
+
|
|
|
+#include <vtkCellArray.h>
|
|
|
+#include <vtkCubeSource.h>
|
|
|
+#include <vtkFloatArray.h>
|
|
|
+#include <vtkNew.h>
|
|
|
+#include <vtkOBJReader.h>
|
|
|
+#include <vtkPointData.h>
|
|
|
+#include <vtkPoints.h>
|
|
|
+#include <vtkPolyData.h>
|
|
|
+#include <vtkPolyDataNormals.h>
|
|
|
+#include <vtkSTLReader.h>
|
|
|
+#include <vtkTriangleFilter.h>
|
|
|
+
|
|
|
+#include <glm/gtc/type_ptr.hpp>
|
|
|
+
|
|
|
+#include <spdlog/spdlog.h>
|
|
|
+
|
|
|
+#include <filesystem>
|
|
|
+
|
|
|
+struct mesh_type::impl {
|
|
|
+
|
|
|
+ struct vertex_info {
|
|
|
+ glm::vec3 position;
|
|
|
+ glm::vec3 normal;
|
|
|
+ };
|
|
|
+
|
|
|
+ size_t triangle_num;
|
|
|
+ smart_buffer<vertex_info> vbo_data;
|
|
|
+ smart_buffer<GLuint> ebo_data;
|
|
|
+
|
|
|
+ size_t modify_id = 0, last_upload_id = 0;
|
|
|
+ GLuint vao = 0, vbo = 0, ebo = 0;
|
|
|
+
|
|
|
+ void upload() {
|
|
|
+ if (last_upload_id == modify_id) [[likely]] return;
|
|
|
+ assert(last_upload_id < modify_id);
|
|
|
+
|
|
|
+ // delete old objects
|
|
|
+ glDeleteVertexArrays(1, &vao);
|
|
|
+ glDeleteBuffers(1, &vbo);
|
|
|
+ glDeleteBuffers(1, &ebo);
|
|
|
+
|
|
|
+ // config vertex buffer
|
|
|
+ glGenBuffers(1, &vbo);
|
|
|
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
+ glBufferData(GL_ARRAY_BUFFER, vbo_data.size(), vbo_data.ptr, GL_STATIC_DRAW);
|
|
|
+
|
|
|
+ // fill element buffer
|
|
|
+ glGenBuffers(1, &ebo);
|
|
|
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
|
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, ebo_data.size(), ebo_data.ptr, GL_STATIC_DRAW);
|
|
|
+
|
|
|
+ // config vertex array
|
|
|
+ glGenVertexArrays(1, &vao);
|
|
|
+ glBindVertexArray(vao);
|
|
|
+ glEnableVertexAttribArray(0);
|
|
|
+ glEnableVertexAttribArray(1);
|
|
|
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(vertex_info), (void *) offsetof(vertex_info, position));
|
|
|
+ glVertexAttribPointer(1, 3, GL_FLOAT, false, sizeof(vertex_info), (void *) offsetof(vertex_info, normal));
|
|
|
+
|
|
|
+ last_upload_id = modify_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ void create_from_poly_data(vtkPolyData *poly) {
|
|
|
+ // calculate vertex normal if needed
|
|
|
+ vtkNew<vtkPolyDataNormals> filter;
|
|
|
+ if (poly->GetPointData()->GetNormals() == nullptr) {
|
|
|
+ filter->SetInputData(poly);
|
|
|
+ filter->Update();
|
|
|
+ poly = filter->GetOutput();
|
|
|
+ }
|
|
|
+
|
|
|
+ // copy vertex data
|
|
|
+ auto points = poly->GetPoints();
|
|
|
+ auto vertex_num = points->GetNumberOfPoints();
|
|
|
+ auto vertex_normal = (vtkFloatArray *) poly->GetPointData()->GetNormals();
|
|
|
+ assert(vertex_normal != nullptr);
|
|
|
+ assert(vertex_normal->GetNumberOfTuples() == vertex_num);
|
|
|
+ vbo_data.create(vertex_num);
|
|
|
+ for (auto k = 0; k < vertex_num; ++k) {
|
|
|
+ glm::dvec3 position, normal;
|
|
|
+ points->GetPoint(k, glm::value_ptr(position));
|
|
|
+ vertex_normal->GetTuple(k, glm::value_ptr(normal));
|
|
|
+ vbo_data.ptr[k].position = position;
|
|
|
+ vbo_data.ptr[k].normal = normal;
|
|
|
+ }
|
|
|
+
|
|
|
+ // copy index data
|
|
|
+ auto cells = poly->GetPolys();
|
|
|
+ triangle_num = cells->GetNumberOfCells();
|
|
|
+ ebo_data.create(triangle_num * 3);
|
|
|
+ vtkNew<vtkIdList> cell;
|
|
|
+ for (auto k = 0; k < triangle_num; ++k) {
|
|
|
+ auto ptr = (GLuint *) ebo_data.ptr + k * 3;
|
|
|
+ cells->GetCellAtId(k, cell);
|
|
|
+ assert(cell->GetNumberOfIds() == 3);
|
|
|
+ for (auto i = 0; i < 3; ++i) {
|
|
|
+ auto vertex_id = cell->GetId(i);
|
|
|
+ assert(vertex_id < vertex_num);
|
|
|
+ ptr[i] = vertex_id;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ++modify_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ void draw() {
|
|
|
+ upload();
|
|
|
+ glBindVertexArray(vao);
|
|
|
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
|
+ glDrawElements(GL_TRIANGLES, 3 * triangle_num, GL_UNSIGNED_INT, nullptr);
|
|
|
+ }
|
|
|
+
|
|
|
+ static impl *from_reader(const char *path,
|
|
|
+ vtkAbstractPolyDataReader *reader) {
|
|
|
+ reader->SetFileName(path);
|
|
|
+ reader->Update();
|
|
|
+ auto ret = new impl{};
|
|
|
+ ret->create_from_poly_data(reader->GetOutput());
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ static impl *from_test_demo() {
|
|
|
+ vtkNew<vtkCubeSource> cube;
|
|
|
+ vtkNew<vtkTriangleFilter> filter;
|
|
|
+ filter->SetInputConnection(cube->GetOutputPort());
|
|
|
+ filter->Update();
|
|
|
+ auto ret = new impl{};
|
|
|
+ ret->create_from_poly_data(filter->GetOutput());
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ static mesh_type *mesh_type_from_reader(const char *path,
|
|
|
+ vtkAbstractPolyDataReader *reader) {
|
|
|
+ auto ret = new mesh_type;
|
|
|
+ ret->pimpl.reset(
|
|
|
+ mesh_type::impl::from_reader(path, reader));
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+mesh_type::mesh_type() = default;
|
|
|
+
|
|
|
+mesh_type::~mesh_type() = default;
|
|
|
+
|
|
|
+mesh_type *mesh_type::from_stl(const char *path) {
|
|
|
+ vtkNew<vtkSTLReader> reader;
|
|
|
+ return impl::mesh_type_from_reader(path, reader);
|
|
|
+}
|
|
|
+
|
|
|
+mesh_type *mesh_type::from_obj(const char *path) {
|
|
|
+ vtkNew<vtkOBJReader> reader;
|
|
|
+ return impl::mesh_type_from_reader(path, reader);
|
|
|
+}
|
|
|
+
|
|
|
+mesh_type *mesh_type::from_test_demo() {
|
|
|
+ auto ret = new mesh_type;
|
|
|
+ ret->pimpl.reset(mesh_type::impl::from_test_demo());
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+namespace mesh_render_impl {
|
|
|
+
|
|
|
+ void ensure_program(smart_program *program) {
|
|
|
+#ifndef NDEBUG
|
|
|
+ GLuint program_id;
|
|
|
+ glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *) &program_id);
|
|
|
+ assert(program_id == program->id);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ void upload_scene_property(smart_program *program,
|
|
|
+ const scene_property &scene_info) {
|
|
|
+ ensure_program(program);
|
|
|
+ program->set_uniform_mat4("camera_mat", scene_info.camera.projection
|
|
|
+ * scene_info.camera.transform);
|
|
|
+ program->set_uniform_vec3("light.direction", scene_info.light.direction);
|
|
|
+ }
|
|
|
+
|
|
|
+ void upload_mesh_property(smart_program *program,
|
|
|
+ const mesh_property &mesh_info) {
|
|
|
+ ensure_program(program);
|
|
|
+ program->set_uniform_mat4("model_mat", mesh_info.transform);
|
|
|
+ program->set_uniform_vec3("material.ambient", mesh_info.material.ambient);
|
|
|
+ program->set_uniform_vec3("material.diffuse", mesh_info.material.diffuse);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+using namespace mesh_render_impl;
|
|
|
+
|
|
|
+struct mesh_render::impl {
|
|
|
+
|
|
|
+ std::unique_ptr<smart_program> program_basic;
|
|
|
+ std::unique_ptr<smart_program> program_mask;
|
|
|
+ std::unique_ptr<smart_program> program_depth_alpha;
|
|
|
+
|
|
|
+ smart_frame_buffer bg_frame;
|
|
|
+ smart_frame_buffer model_frame;
|
|
|
+
|
|
|
+ texture_render tex_render;
|
|
|
+
|
|
|
+ impl() {
|
|
|
+ std::filesystem::path shader_folder = "/home/tpx/project/TransparentAR/src/shader";
|
|
|
+
|
|
|
+ auto vert_basic = shader_folder / "mesh_render.vert";
|
|
|
+ auto frag_basic = shader_folder / "mesh_render.frag";
|
|
|
+ program_basic.reset(smart_program::create(vert_basic.c_str(),
|
|
|
+ frag_basic.c_str()));
|
|
|
+
|
|
|
+ auto vert_mask = shader_folder / "mesh_render_mask.vert";
|
|
|
+ auto frag_mask = shader_folder / "mesh_render_mask.frag";
|
|
|
+ program_mask.reset(smart_program::create(vert_mask.c_str(),
|
|
|
+ frag_mask.c_str()));
|
|
|
+
|
|
|
+ auto frag_depth_alpha = shader_folder / "mesh_render_depth_alpha.frag";
|
|
|
+ program_depth_alpha.reset(smart_program::create(vert_basic.c_str(),
|
|
|
+ frag_depth_alpha.c_str()));
|
|
|
+ }
|
|
|
+
|
|
|
+ void render(mesh_type *mesh,
|
|
|
+ const mesh_property &mesh_info,
|
|
|
+ const scene_property &scene_info) {
|
|
|
+ program_basic->use();
|
|
|
+ upload_mesh_property(program_basic.get(), mesh_info);
|
|
|
+ upload_scene_property(program_basic.get(), scene_info);
|
|
|
+ mesh->pimpl->draw();
|
|
|
+ }
|
|
|
+
|
|
|
+ void render(mesh_type *model, const mesh_property &model_info,
|
|
|
+ mesh_type *bg_mesh, const mesh_property &bg_info,
|
|
|
+ const scene_property &scene_info,
|
|
|
+ float alpha_factor, bool show_bg) {
|
|
|
+
|
|
|
+ // config frame buffers
|
|
|
+ GLint viewport[4]; // x, y, width, height
|
|
|
+ glGetIntegerv(GL_VIEWPORT, viewport);
|
|
|
+ auto view_size = cv::Size{viewport[2], viewport[3]};
|
|
|
+ bg_frame.create(view_size);
|
|
|
+ model_frame.create(view_size);
|
|
|
+
|
|
|
+ // render background
|
|
|
+ bg_frame.bind();
|
|
|
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
+ if (show_bg) {
|
|
|
+ render(bg_mesh, bg_info, scene_info);
|
|
|
+ } else { // use a simpler program
|
|
|
+ auto bg_mat = scene_info.camera.projection
|
|
|
+ * scene_info.camera.transform
|
|
|
+ * bg_info.transform;
|
|
|
+ program_mask->use();
|
|
|
+ program_mask->set_uniform_mat4("transform_mat", bg_mat);
|
|
|
+ bg_mesh->pimpl->draw();
|
|
|
+ }
|
|
|
+ bg_frame.unbind();
|
|
|
+
|
|
|
+ // render model
|
|
|
+ model_frame.bind();
|
|
|
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
+ render(model, model_info, scene_info);
|
|
|
+ model_frame.unbind();
|
|
|
+
|
|
|
+ // real draw frame
|
|
|
+ GLboolean depth_enable;
|
|
|
+ glGetBooleanv(GL_DEPTH_TEST, &depth_enable);
|
|
|
+ glDisable(GL_DEPTH_TEST);
|
|
|
+ if (show_bg) {
|
|
|
+ tex_render.render(bg_frame.color_tex());
|
|
|
+ }
|
|
|
+ tex_render.render_depth_alpha(model_frame.color_tex(), model_frame.depth_tex(), bg_frame.depth_tex(),
|
|
|
+ scene_info.camera.projection, alpha_factor);
|
|
|
+ if (depth_enable) {
|
|
|
+ glEnable(GL_DEPTH_TEST);
|
|
|
+ }
|
|
|
+
|
|
|
+// // render model
|
|
|
+// program_depth_alpha->use();
|
|
|
+// upload_mesh_property(program_depth_alpha.get(), model_info);
|
|
|
+// upload_scene_property(program_depth_alpha.get(), scene_info);
|
|
|
+//
|
|
|
+// GLfloat depth_range[2];
|
|
|
+// glGetFloatv(GL_DEPTH_RANGE, depth_range);
|
|
|
+// auto viewport_min = glm::vec3{viewport[0], viewport[1], depth_range[0]};
|
|
|
+// auto viewport_max = glm::vec3{viewport[0] + viewport[2], // x + width
|
|
|
+// viewport[1] + viewport[3], // y + height
|
|
|
+// depth_range[1]};
|
|
|
+// auto viewport_middle = 0.5f * (viewport_max + viewport_min);
|
|
|
+// auto viewport_half_inv = 2.0f / (viewport_max - viewport_min);
|
|
|
+// auto project_inv = glm::inverse(scene_info.camera.projection);
|
|
|
+// program_depth_alpha->set_uniform_vec3("viewport.middle", viewport_middle);
|
|
|
+// program_depth_alpha->set_uniform_vec3("viewport.half_inv", viewport_half_inv);
|
|
|
+// program_depth_alpha->set_uniform_mat4("viewport.proj_inv", project_inv);
|
|
|
+//
|
|
|
+// alpha_factor = alpha_factor / (1.0f - alpha_factor); // [0, 1] -> [0, +inf)
|
|
|
+// program_depth_alpha->set_uniform_f("alpha_factor", alpha_factor);
|
|
|
+//
|
|
|
+// glActiveTexture(GL_TEXTURE0 + 0);
|
|
|
+// glBindTexture(GL_TEXTURE_2D, bg_frame.depth_tex());
|
|
|
+// program_depth_alpha->set_uniform_i("bg_depth_tex", 0);
|
|
|
+//
|
|
|
+// glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
+// model->pimpl->draw();
|
|
|
+ }
|
|
|
+
|
|
|
+};
|
|
|
+
|
|
|
+mesh_render::mesh_render()
|
|
|
+ : pimpl(std::make_unique<impl>()) {}
|
|
|
+
|
|
|
+mesh_render::~mesh_render() = default;
|
|
|
+
|
|
|
+void mesh_render::render(mesh_type *mesh,
|
|
|
+ const mesh_property &mesh_info,
|
|
|
+ const scene_property &scene_info) {
|
|
|
+ pimpl->render(mesh, mesh_info, scene_info);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+mesh_render::render(mesh_type *model, const mesh_property &model_info,
|
|
|
+ mesh_type *bg_mesh, const mesh_property &bg_info,
|
|
|
+ const scene_property &scene_info,
|
|
|
+ float alpha_factor, bool show_bg) {
|
|
|
+ pimpl->render(model, model_info, bg_mesh, bg_info,
|
|
|
+ scene_info, alpha_factor, show_bg);
|
|
|
+}
|